revert between 56095 -> 55830 in arch
[AROS.git] / rom / filesys / SFS / FS / filesystemmain.c
blob056568cfdc75385385437cb8ef8ca688ccc9c52c
1 #include "asmsupport.h"
3 #include <exec/types.h>
4 #include <clib/macros.h> // MAX, MIN & ABS :-)
5 #include <devices/input.h>
6 #include <devices/inputevent.h>
7 #include <devices/timer.h>
8 #include <devices/trackdisk.h>
9 #include <dos/dos.h>
10 #include <dos/dosextens.h>
11 #include <dos/dostags.h>
12 #include <dos/exall.h>
13 #include <dos/filehandler.h>
14 #include <dos/notify.h>
15 #include <exec/errors.h>
16 #include <exec/interrupts.h>
17 #include <exec/io.h>
18 #include <exec/lists.h>
19 #include <exec/memory.h>
20 #include <exec/nodes.h>
21 #include <exec/resident.h>
22 #include <libraries/iffparse.h>
23 #include <resources/filesysres.h>
24 #include <proto/dos.h>
25 #include <proto/exec.h>
26 #include <proto/intuition.h>
27 #include <proto/timer.h>
28 #include <proto/utility.h>
30 #define DEBUG 1
31 #define DEBUGCODE
33 #include <math.h>
35 #include "sysdep.h"
36 #include "fs.h"
37 #include "adminspaces.h"
38 #include "bitmap.h"
39 #include "btreenodes.h"
40 #include "locks.h"
41 #include "nodes.h"
42 #include "objects.h"
43 #include "transactions.h"
45 #include "adminspaces_protos.h"
46 #include "bitmap_protos.h"
47 #include "btreenodes_protos.h"
48 #include "cachebuffers_protos.h"
49 #include "debug.h"
50 #include "locks_protos.h"
51 #include "nodes_protos.h"
52 #include "objects_protos.h"
53 #include "packets.h"
54 #include "query.h"
55 #include "support_protos.h"
56 #include "transactions_protos.h"
57 #include "req_protos.h"
59 #include <string.h>
61 #include "cachedio_protos.h"
62 #include "deviceio_protos.h"
64 static LONG fillgap(BLCK key);
65 LONG step(void);
67 #define BNODE
69 #define BITMAPFILL 0xFFFFFFFF /* should be 0xFFFFFFFF ! Careful.. admin containers are 32 blocks! */
71 /* defines: */
73 #define BLCKFACCURACY (5) /* 2^5 = 32 */
75 #define ID_BUSY AROS_LONG2BE(MAKE_ID('B','U','S','Y'))
77 /* Our own usage of NotifyRequest private data */
78 #define nr_Next nr_Reserved[2]
79 #define nr_Prev nr_Reserved[3]
81 /* structs */
82 #define SFSM_ADD_VOLUMENODE (1)
83 #define SFSM_REMOVE_VOLUMENODE (2)
85 struct SFSMessage {
86 struct Message msg;
87 ULONG command;
88 IPTR data;
89 LONG errorcode;
92 struct DefragmentStep {
93 ULONG id; // id of the step ("MOVE", "DONE" or 0)
94 ULONG length; // length in longwords (can be 0)
95 ULONG data[0]; // size of this array is determined by length.
99 /* global variables */
101 #include "globals.h"
103 #ifndef __AROS__
104 struct SFSBase *globals=NULL;
105 #endif
107 /* Prototypes */
109 static struct DosPacket *getpacket(struct Process *);
110 static struct DosPacket *waitpacket(struct Process *);
111 static void returnpacket(SIPTR,LONG);
112 static void sdlhtask(void);
114 /* Prototypes of cachebuffer related functions */
116 LONG readcachebuffercheck(struct CacheBuffer **,ULONG,ULONG);
117 void outputcachebuffer(struct CacheBuffer *cb);
119 /* Prototypes of node related functions */
121 LONG deleteextents(ULONG key);
122 static LONG findextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode);
123 static LONG createextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode);
125 /* Prototypes of debug functions */
127 ULONG calcchecksum(void);
128 void checksum(void);
130 /* Misc prototypes */
132 void starttimeout(void);
133 LONG flushcaches(void);
134 void invalidatecaches(void);
136 BOOL freeupspace(void);
138 BOOL checkchecksum(struct CacheBuffer *);
139 void setchecksum(struct CacheBuffer *);
141 void checknotifyforobject(struct CacheBuffer *cb,struct fsObject *o,UBYTE notifyparent);
142 void checknotifyforpath(UBYTE *path,UBYTE notifyparent);
143 void notify(struct NotifyRequest *nr);
144 UBYTE *fullpath(struct CacheBuffer *cbstart,struct fsObject *o);
146 LONG initdisk(void);
147 static void deinitdisk(void);
149 LONG handlesimplepackets(struct DosPacket *packet);
150 static LONG dumppackets(struct DosPacket *packet,LONG);
151 #ifdef DEBUGCODE
152 static void dumppacket(void);
153 #endif
154 static void actioncurrentvolume(struct DosPacket *);
155 static void actionsamelock(struct DosPacket *);
156 static void actiondiskinfo(struct DosPacket *);
157 static void fillinfodata(struct InfoData *);
158 static void fillfib(struct FileInfoBlock *,struct fsObject *);
159 static void diskchangenotify(ULONG class);
161 /* Prototypes of high-level filesystem functions */
163 LONG setfilesize(struct ExtFileLock *lock,ULONG bytes);
164 static LONG seek(struct ExtFileLock *lock,ULONG offset);
165 LONG seektocurrent(struct ExtFileLock *lock);
166 LONG seekextent(struct ExtFileLock *lock,ULONG offset,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_ebn,ULONG *returned_extentoffset);
167 void seekforward(struct ExtFileLock *lock, UWORD ebn_blocks, BLCK ebn_next, ULONG bytestoseek);
168 LONG writetofile(struct ExtFileLock *lock, UBYTE *buffer, ULONG bytestowrite);
170 static LONG extendblocksinfile(struct ExtFileLock *lock,ULONG blocks);
171 static LONG addblocks(UWORD blocks, BLCK newspace, NODE objectnode, BLCK *io_lastextentbnode);
172 LONG deletefileslowly(struct CacheBuffer *cbobject, struct fsObject *o);
174 void mainloop(void);
176 /* ASM prototypes */
178 #define MAJOR_VERSION (1)
179 #define MINOR_VERSION (84)
181 #ifdef __GNUC__
182 const char ver_version[]="\0$VER: " PROGRAMNAMEVER " 1.84 (" ADATE ")\r\n";
183 #else
184 static const char ver_version[]={"\0$VER: " PROGRAMNAMEVER " 1.84 " __AMIGADATE__ "\r\n"};
185 #endif
187 #ifdef __AROS__
188 /* AROS builds in a 'struct Resident' automatically
190 #else
191 /* ROMTag is useful for C:Version. */
192 #define res_Init NULL
194 const struct Resident resident =
196 RTC_MATCHWORD,
197 &resident,
198 (APTR)&resident + sizeof(struct Resident),
199 RTF_COLDSTART,
200 MAJOR_VERSION,
202 -81,
203 PROGRAMNAME,
204 &ver_version[7],
205 res_Init
207 #endif
209 /* Main */
211 #ifndef __AROS__
212 extern const RESBASE;
213 extern const RESLEN;
214 extern const _LinkerDB;
215 extern const NEWDATAL;
216 #endif
218 LONG mainprogram(struct ExecBase *);
220 #ifndef __AROS__
221 #undef SysBase
222 #endif
224 #ifndef __AROS__
225 LONG __saveds trampoline(void)
227 struct ExecBase *sBase = (*((struct ExecBase **)4));
229 return mainprogram(sBase);
232 LONG start(void)
234 return(STACKSWAP(4096, trampoline));
235 /* if(STACKSWAP()==0) {
236 return(ERROR_NO_FREE_STORE);
239 return(mainprogram()); */
241 #endif
243 void request2(UBYTE *text);
245 // #define STARTDEBUG
247 LONG mainprogram(struct ExecBase *SysBase)
249 #ifndef __AROS__
250 ULONG reslen;
251 APTR old_a4;
252 APTR newdata;
253 #endif
255 D(bug("[SFS] Filesystem main\n"));
257 globals = AllocMem(sizeof(struct SFSBase), MEMF_PUBLIC | MEMF_CLEAR);
258 #ifndef __AROS__
259 globals->sysBase = SysBase;
260 #endif
261 initGlobals();
263 #ifndef __AROS__
264 #undef SysBase
265 #define SysBase (globals->sysBase)
267 old_a4=(APTR)getreg(REG_A4);
268 reslen=((ULONG)&RESLEN-(ULONG)old_a4)+64;
270 newdata=AllocMem(reslen,MEMF_CLEAR|MEMF_PUBLIC);
272 CopyMem(old_a4,newdata,*(((ULONG *)old_a4)-2));
274 putreg(REG_A4,(LONG)newdata);
275 #endif
277 if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37))!=0) {
278 D(bug("[SFS] DOSBase = %p\n", DOSBase));
280 globals->mytask=(struct Process *)FindTask(0);
281 D(bug("[SFS] mytask = %p\n", globals->mytask));
283 globals->packet=waitpacket(globals->mytask);
284 D(bug("[SFS] packet = %p\n", globals->packet));
286 globals->devnode=(struct DeviceNode *)BADDR(globals->packet->dp_Arg3);
287 globals->devnode->dn_Task=&globals->mytask->pr_MsgPort;
288 globals->startupmsg=BADDR(globals->devnode->dn_Startup);
289 D(bug("[SFS] devnode = %p\n", globals->devnode));
290 D(bug("[SFS] startupmsg = %p\n", globals->startupmsg));
292 if(initcachebuffers()==0) {
294 if((IntuitionBase=(APTR)OpenLibrary("intuition.library",37))!=0) {
296 #ifdef STARTDEBUG
297 dreq("(1) Filesystem initializing...");
298 #endif
300 if((UtilityBase=(APTR)OpenLibrary("utility.library",37))!=0) {
302 /* Create a msgport and iorequest for opening timer.device */
304 if((globals->msgportnotify=CreateMsgPort())!=0) {
305 if((globals->msgporttimer=CreateMsgPort())!=0) {
306 if((globals->msgportflushtimer=CreateMsgPort())!=0) {
307 if((globals->inactivitytimer_ioreq=(struct timerequest *)CreateIORequest(globals->msgporttimer, sizeof(struct timerequest)))!=0) {
308 if((globals->activitytimer_ioreq=(struct timerequest *)CreateIORequest(globals->msgportflushtimer, sizeof(struct timerequest)))!=0) {
310 #ifdef STARTDEBUG
311 dreq("(2) Message ports and iorequests created");
312 #endif
314 if(OpenDevice("timer.device",UNIT_VBLANK,&globals->inactivitytimer_ioreq->tr_node,0)==0) {
315 if(OpenDevice("timer.device",UNIT_VBLANK,&globals->activitytimer_ioreq->tr_node,0)==0) {
317 globals->dosenvec=(struct DosEnvec *)BADDR(globals->startupmsg->fssm_Environ);
319 globals->timerBase=(struct Device *)globals->inactivitytimer_ioreq->tr_node.io_Device;
321 /* Create a msgport and iorequest for opening the filesystem device */
323 initlist((struct List *)&globals->globalhandles);
325 #ifdef STARTDEBUG
326 dreq("(3) Timer.device opened");
327 #endif
329 if(initcachedio(AROS_BSTR_ADDR(globals->startupmsg->fssm_Device), globals->startupmsg->fssm_Unit, globals->startupmsg->fssm_Flags, globals->dosenvec)==0) {
330 #ifdef STARTDEBUG
331 dreq("(4) Cached IO layer started");
332 #endif
334 globals->shifts_block32=globals->shifts_block-BLCKFACCURACY;
336 globals->mask_block32=(1<<globals->shifts_block32)-1;
337 globals->blocks_inbitmap=(globals->bytes_block-sizeof(struct fsBitmap))<<3; /* must be a multiple of 32 !! */
338 globals->blocks_bitmap=(globals->blocks_total+globals->blocks_inbitmap-1)/globals->blocks_inbitmap;
339 globals->blocks_admin=32;
341 globals->blocks_reserved_start=MAX(globals->dosenvec->de_Reserved,1);
342 globals->blocks_reserved_end=MAX(globals->dosenvec->de_PreAlloc,1);
345 ULONG blocks512, reserve;
347 blocks512=globals->blocks_total<<(globals->shifts_block-9);
348 reserve=SQRT(blocks512);
349 reserve=(reserve<<2) + reserve;
351 if(reserve > blocks512/100) { // Do not use more than 1% of the disk.
352 reserve = blocks512/100;
355 if(reserve < globals->blocks_admin) { // Use atleast 32 blocks, even if it is more than 1%.
356 reserve = globals->blocks_admin;
357 if(reserve > globals->blocks_total>>1) {
358 reserve = 0;
362 globals->block_rovingblockptr=globals->blocks_reserved_start + globals->blocks_admin + globals->blocks_bitmap + reserve;
364 _DEBUG(("RovingBlockPtr = %ld, reserve = %ld\n", globals->block_rovingblockptr, reserve));
366 // block_rovingblockptr=0;
369 globals->mask_debug=0x00000000;
371 globals->node_containers=(globals->bytes_block-sizeof(struct fsNodeContainer))/sizeof(BLCKn);
373 addchangeint((struct Task *)globals->mytask, 1<<globals->mytask->pr_MsgPort.mp_SigBit);
375 _DEBUG(("Initializing transactions\n"));
377 if(inittransactions()==0) {
379 #ifdef STARTDEBUG
380 dreq("(5) Transaction layer started");
381 #endif
383 if(addcachebuffers(globals->dosenvec->de_NumBuffers)==0) {
385 #ifdef STARTDEBUG
386 dreq("(6) Filesystem started succesfully!");
387 #endif
389 /* return startup-packet, the handler runs now */
391 _DEBUG(("Filesystem started! Volumenode = %ld\n",globals->volumenode));
392 _DEBUG(("Mountlist entry says: Allocate %ld buffers of memtype 0x%08lx\n",globals->dosenvec->de_NumBuffers,globals->dosenvec->de_BufMemType));
394 // returnpacket(DOSTRUE,0); // Sep 19 1999: Moved down again.
396 _DEBUG(("CreateNewProc..."));
398 const struct TagItem tags[]=
400 {NP_Entry , (IPTR)sdlhtask },
401 {NP_Name , (IPTR)"SFS DosList handler"},
402 {NP_Priority , 19 },
403 {TAG_DONE , 0 }
406 if(CreateNewProc(tags)!=0) {
408 _DEBUG(("ok\n"));
410 while((globals->sdlhport=FindPort("SFS DosList handler"))==0) {
411 Delay(2);
414 if(isdiskpresent()!=FALSE) {
415 #ifdef STARTDEBUG
416 dreq("There is a disk present.");
417 #endif
419 initdisk();
421 else {
422 #ifdef STARTDEBUG
423 dreq("No disk inserted.");
424 #endif
426 globals->disktype=ID_NO_DISK_PRESENT;
429 returnpacket(DOSTRUE,0); // Jul 4 1999: Moved up...
431 #ifdef STARTDEBUG
432 dreq("(7) Informed DOS about the new partition!");
433 #endif
435 mainloop();
439 cleanuptransactions();
442 removechangeint();
443 cleanupcachedio();
445 CloseDevice(&globals->activitytimer_ioreq->tr_node);
447 CloseDevice(&globals->inactivitytimer_ioreq->tr_node);
449 DeleteIORequest((struct IORequest *)globals->activitytimer_ioreq);
451 DeleteIORequest((struct IORequest *)globals->inactivitytimer_ioreq);
453 DeleteMsgPort(globals->msgportflushtimer);
455 DeleteMsgPort(globals->msgporttimer);
457 DeleteMsgPort(globals->msgportnotify);
459 CloseLibrary((struct Library *)UtilityBase);
462 #ifdef STARTDEBUG
463 dreq("Filesystem failed.. exiting.");
464 #endif
466 CloseLibrary((struct Library *)IntuitionBase);
470 _DEBUG(("Returning startup packet with DOSFALSE\n"));
472 returnpacket(DOSFALSE,ERROR_NO_FREE_STORE);
474 CloseLibrary((struct Library *)DOSBase);
477 FreeMem(globals, sizeof(struct SFSBase));
478 _DEBUG(("Exiting filesystem\n"));
480 return(ERROR_NO_FREE_STORE);
484 void mainloop(void) {
485 ULONG signalbits;
486 struct MsgPort *msgportpackets;
487 struct Message *msg;
489 msgportpackets=&globals->mytask->pr_MsgPort; /* get port of our process */
490 signalbits=1<<msgportpackets->mp_SigBit;
491 signalbits|=1<<globals->msgporttimer->mp_SigBit;
492 signalbits|=1<<globals->msgportnotify->mp_SigBit;
493 signalbits|=1<<globals->msgportflushtimer->mp_SigBit;
495 #ifdef STARTDEBUG
496 dreq("Entering packet loop.");
497 #endif
499 for(;;) {
501 Wait(signalbits);
503 do {
504 while((msg=GetMsg(globals->msgportflushtimer))!=0) {
505 _TDEBUG(("mainloop: activity timeout -> flushed transaction\n"));
506 flushtransaction();
507 globals->activitytimeractive=FALSE;
510 while((msg=GetMsg(globals->msgporttimer))!=0) {
511 if(globals->timerreset==TRUE) {
512 /* There was another request during the timeout/2 period, so we extend the timeout a bit longer. */
513 globals->pendingchanges=FALSE;
514 starttimeout();
516 else {
517 _TDEBUG(("mainloop: inactivity timeout -> flushed transaction\n"));
518 flushcaches();
519 globals->pendingchanges=FALSE;
523 while((msg=GetMsg(globals->msgportnotify))!=0) {
524 FreeMem(msg,sizeof(struct NotifyMessage));
527 if(getchange()!=0) {
529 /* The disk was inserted or removed! */
531 _DEBUG(("mainloop: disk inserted or removed\n"));
533 if(isdiskpresent()==FALSE) {
534 /* Disk was removed */
536 globals->disktype=ID_NO_DISK_PRESENT; /* Must be put before deinitdisk() */
537 deinitdisk();
539 else {
540 /* Disk was inserted */
542 initdisk();
546 if((msg=GetMsg(msgportpackets))!=0) {
549 // diskstate=writeprotection(); /* Don't do this too often!! It takes LOADS of time for scsi.device. */
552 #ifdef CHECKCODE_SLOW
554 struct CacheBuffer *cb;
556 cb=(struct CacheBuffer *)cblrulist.mlh_Head;
558 while(cb->node.mln_Succ!=0) {
559 if(cb->locked!=0) {
560 request(PROGRAMNAME " request","%s\n"\
561 "mainloop: There was a locked CacheBuffer (lockcount = %ld, block = %ld, type = 0x%08lx)!\n"\
562 "Nothing bad will happen, but let the author know.\n",
563 "Ok",AROS_BSTR_ADDR(devnode->dn_Name), cb->locked, cb->blckno, *((ULONG *)cb->data));
566 cb->locked=0; /* Nothing remains locked */
569 cb=(struct CacheBuffer *)(cb->node.mln_Succ);
572 #endif
575 globals->packet=(struct DosPacket *)msg->mn_Node.ln_Name;
577 #ifdef STARTDEBUG
578 dreq("Received packet 0x%08lx, %ld.",globals->packet,globals->packet->dp_Type);
579 #endif
581 switch(globals->packet->dp_Type) {
582 case ACTION_SFS_SET:
584 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
585 struct TagItem *tag;
587 while((tag=NextTagItem(&taglist))!=NULL) {
588 LONG data=tag->ti_Data;
590 switch(tag->ti_Tag) {
591 case ASS_MAX_NAME_LENGTH:
592 if(data >= 30 && data <= 100) {
593 globals->max_name_length=data;
595 break;
596 case ASS_ACTIVITY_FLUSH_TIMEOUT:
597 if(data >= 5 && data <= 120) {
598 globals->activity_timeout=data;
600 break;
601 case ASS_INACTIVITY_FLUSH_TIMEOUT:
602 if(data >= 1 && data <= 5) {
603 globals->inactivity_timeout=data;
605 break;
609 returnpacket(DOSTRUE,0);
611 break;
612 case ACTION_SFS_QUERY:
614 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
615 struct TagItem *tag;
617 while((tag=NextTagItem(&taglist)))
619 switch(tag->ti_Tag)
621 case ASQ_START_BYTEH:
622 tag->ti_Data = globals->byte_low >> 32;
623 break;
625 case ASQ_START_BYTEL:
627 * Explicitly cast to ULONG here because on 64 bits
628 * ti_Data is 64-bit wide, and this can confuse programs.
630 tag->ti_Data = (ULONG)globals->byte_low;
631 break;
633 case ASQ_END_BYTEH:
634 tag->ti_Data = globals->byte_high >> 32;
635 break;
637 case ASQ_END_BYTEL:
638 tag->ti_Data = (ULONG)globals->byte_high;
639 break;
641 case ASQ_DEVICE_API:
642 tag->ti_Data=deviceapiused();
643 break;
644 case ASQ_BLOCK_SIZE:
645 tag->ti_Data=globals->bytes_block;
646 break;
647 case ASQ_TOTAL_BLOCKS:
648 tag->ti_Data=globals->blocks_total;
649 break;
650 case ASQ_ROOTBLOCK:
651 tag->ti_Data=globals->block_root;
652 break;
653 case ASQ_ROOTBLOCK_OBJECTNODES:
654 tag->ti_Data=globals->block_objectnoderoot;
655 break;
656 case ASQ_ROOTBLOCK_EXTENTS:
657 tag->ti_Data=globals->block_extentbnoderoot;
658 break;
659 case ASQ_FIRST_BITMAP_BLOCK:
660 tag->ti_Data=globals->block_bitmapbase;
661 break;
662 case ASQ_FIRST_ADMINSPACE:
663 tag->ti_Data=globals->block_adminspace;
664 break;
665 case ASQ_CACHE_LINES:
666 tag->ti_Data=queryiocache_lines();
667 break;
668 case ASQ_CACHE_READAHEADSIZE:
669 tag->ti_Data=queryiocache_readaheadsize();
670 break;
671 case ASQ_CACHE_MODE:
672 tag->ti_Data=queryiocache_copyback();
673 break;
674 case ASQ_CACHE_BUFFERS:
675 tag->ti_Data=globals->totalbuffers;
676 break;
677 case ASQ_CACHE_ACCESSES:
678 tag->ti_Data=globals->statistics.cache_accesses;
679 break;
680 case ASQ_CACHE_MISSES:
681 tag->ti_Data=globals->statistics.cache_misses;
682 break;
683 case ASQ_OPERATIONS_DECODED:
684 tag->ti_Data=globals->statistics.cache_operationdecode;
685 break;
686 case ASQ_EMPTY_OPERATIONS_DECODED:
687 tag->ti_Data=globals->statistics.cache_emptyoperationdecode;
688 break;
689 case ASQ_IS_CASESENSITIVE:
690 tag->ti_Data=globals->is_casesensitive;
691 break;
692 case ASQ_HAS_RECYCLED:
693 tag->ti_Data=globals->has_recycled;
694 break;
695 case ASQ_VERSION:
696 tag->ti_Data=MAJOR_VERSION * 65536 + MINOR_VERSION;
697 break;
698 case ASQ_MAX_NAME_LENGTH:
699 tag->ti_Data=globals->max_name_length;
700 break;
701 case ASQ_ACTIVITY_FLUSH_TIMEOUT:
702 tag->ti_Data=globals->activity_timeout;
703 break;
704 case ASQ_INACTIVITY_FLUSH_TIMEOUT:
705 tag->ti_Data=globals->inactivity_timeout;
706 break;
710 returnpacket(DOSTRUE,0);
712 break;
713 case ACTION_SET_DEBUG:
714 _DEBUG(("New debug level set to 0x%08lx!\n",globals->packet->dp_Arg1));
716 globals->mask_debug=globals->packet->dp_Arg1;
718 returnpacket(DOSTRUE,0);
720 break;
721 case ACTION_SET_CACHE:
722 _DEBUG(("ACTION_SET_CACHE\n"));
724 LONG errorcode;
726 if((errorcode=setiocache(globals->packet->dp_Arg1, globals->packet->dp_Arg2, globals->packet->dp_Arg3 & 1))!=0) {
727 returnpacket(DOSFALSE, errorcode);
729 else {
730 returnpacket(DOSTRUE, 0);
734 break;
735 case ACTION_SFS_FORMAT:
736 case ACTION_FORMAT:
737 _DEBUG(("ACTION_FORMAT\n"));
740 struct CacheBuffer *cb;
741 ULONG currentdate;
742 BLCK block_recycled;
743 LONG errorcode=0;
745 UBYTE *name=0;
746 UBYTE *recycledname=".recycled";
747 BYTE casesensitive=FALSE;
748 BYTE norecycled=FALSE;
749 BYTE showrecycled=FALSE;
750 currentdate=getdate();
752 if(globals->packet->dp_Type==ACTION_SFS_FORMAT) {
753 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
754 struct TagItem *tag;
756 while((tag=NextTagItem(&taglist))) {
757 switch(tag->ti_Tag) {
758 case ASF_NAME:
759 name=(UBYTE *)tag->ti_Data;
760 break;
761 case ASF_RECYCLEDNAME:
762 recycledname=(UBYTE *)tag->ti_Data;
763 break;
764 case ASF_CASESENSITIVE:
765 casesensitive=tag->ti_Data;
766 break;
767 case ASF_NORECYCLED:
768 norecycled=tag->ti_Data;
769 break;
770 case ASF_SHOWRECYCLED:
771 showrecycled=tag->ti_Data;
772 break;
776 else {
777 copybstrasstr((BSTR)globals->packet->dp_Arg1, globals->string, 30);
778 name=globals->string;
781 /* Global block numbers */
783 globals->block_adminspace=globals->blocks_reserved_start;
784 globals->block_root=globals->blocks_reserved_start+1;
785 globals->block_extentbnoderoot=globals->block_root+3;
786 globals->block_bitmapbase=globals->block_adminspace+globals->blocks_admin;
787 globals->block_objectnoderoot=globals->block_root+4;
789 /* Temporary block numbers */
791 block_recycled=globals->block_root+5;
793 cb=getcachebuffer();
795 if(isvalidcomponentname(name)==FALSE || isvalidcomponentname(recycledname)==FALSE) {
796 errorcode=ERROR_INVALID_COMPONENT_NAME;
799 if(errorcode==0) {
800 struct fsAdminSpaceContainer *ac=cb->data;
802 _DEBUG(("ACTION_FORMAT: Creating AdminSpace container block\n"));
804 /* Create AdminSpaceContainer block */
806 cb->blckno=globals->block_adminspace;
807 clearcachebuffer(cb);
809 ac->bheader.id=ADMINSPACECONTAINER_ID;
810 ac->bheader.be_ownblock=L2BE(globals->block_adminspace);
811 ac->bits=globals->blocks_admin;
812 ac->adminspace[0].be_space=L2BE(globals->block_adminspace);
814 if(norecycled==FALSE) {
815 /* NOT HASHED RECYCLED
816 ac->adminspace[0].be_bits=L2BE(0xFF000000); // admin + root + hashtable + restore + 2 * nodecontainers + recycled + hash
818 ac->adminspace[0].be_bits=L2BE(0xFE000000); /* admin + root + hashtable + restore + 2 * nodecontainers + recycled */
820 else {
821 ac->adminspace[0].be_bits=L2BE(0xFC000000); /* admin + root + hashtable + restore + 2 * nodecontainers */
824 setchecksum(cb);
825 errorcode=writecachebuffer(cb);
828 if(errorcode==0) {
829 struct fsObjectContainer *oc=cb->data;
830 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
832 _DEBUG(("ACTION_FORMAT: Creating Root block\n"));
834 /* Create Root block */
836 cb->blckno=globals->block_root;
837 clearcachebuffer(cb);
839 oc->bheader.id=OBJECTCONTAINER_ID;
840 oc->bheader.be_ownblock=L2BE(globals->block_root);
841 oc->object[0].be_protection=L2BE(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
842 oc->object[0].be_datemodified=L2BE(currentdate);
843 oc->object[0].bits=OTYPE_DIR;
844 oc->object[0].be_objectnode=L2BE(ROOTNODE);
846 oc->object[0].object.dir.be_hashtable=L2BE(globals->block_root+1);
848 if(norecycled==FALSE) {
849 oc->object[0].object.dir.be_firstdirblock=L2BE(block_recycled);
852 copystr(name, oc->object[0].name, 30);
854 ri->be_freeblocks=L2BE(globals->blocks_total-globals->blocks_admin-globals->blocks_reserved_start-globals->blocks_reserved_end-globals->blocks_bitmap);
855 ri->be_datecreated=oc->object[0].be_datemodified; // BE-BE copy
857 setchecksum(cb);
858 errorcode=writecachebuffer(cb);
861 if(errorcode==0) {
862 struct fsHashTable *ht=cb->data;
864 _DEBUG(("ACTION_FORMAT: Creating Root's HashTable block\n"));
866 /* Create Root's HashTable block */
868 cb->blckno=globals->block_root+1;
869 clearcachebuffer(cb);
871 ht->bheader.id=HASHTABLE_ID;
872 ht->bheader.be_ownblock=L2BE(globals->block_root+1);
873 ht->be_parent=L2BE(ROOTNODE);
875 if(norecycled==FALSE) {
876 ht->be_hashentry[HASHCHAIN(hash(".recycled", casesensitive))]=L2BE(RECYCLEDNODE);
879 setchecksum(cb);
880 errorcode=writecachebuffer(cb);
883 if(errorcode==0) {
884 struct fsBlockHeader *bh=cb->data;
886 _DEBUG(("ACTION_FORMAT: Creating Transaction block\n"));
888 /* Create empty block as a placeholder for the TransactionFailure block. */
890 cb->blckno=globals->block_root+2;
891 clearcachebuffer(cb);
893 bh->id=TRANSACTIONOK_ID;
894 bh->be_ownblock=L2BE(globals->block_root+2);
896 setchecksum(cb);
897 errorcode=writecachebuffer(cb);
900 if(errorcode==0) {
901 struct fsBNodeContainer *bnc=cb->data;
902 struct BTreeContainer *btc=&bnc->btc;
904 _DEBUG(("ACTION_FORMAT: Creating ExtentNode root block\n"));
906 /* Create NodeContainer block for ExtentNodes */
908 cb->blckno=globals->block_extentbnoderoot;
909 clearcachebuffer(cb);
911 bnc->bheader.id=BNODECONTAINER_ID;
912 bnc->bheader.be_ownblock=L2BE(globals->block_extentbnoderoot);
914 btc->isleaf=TRUE;
915 btc->be_nodecount=0;
916 btc->nodesize=sizeof(struct fsExtentBNode);
918 setchecksum(cb);
919 errorcode=writecachebuffer(cb);
922 if(errorcode==0) {
923 struct fsNodeContainer *nc=cb->data;
924 struct fsObjectNode *on;
926 _DEBUG(("ACTION_FORMAT: Creating ObjectNode root block\n"));
928 /* Create NodeContainer block for ObjectNodes */
930 cb->blckno=globals->block_objectnoderoot;
931 clearcachebuffer(cb);
933 nc->bheader.id=NODECONTAINER_ID;
934 nc->bheader.be_ownblock=L2BE(globals->block_objectnoderoot);
936 nc->be_nodenumber=L2BE(1); /* objectnode 0 is reserved :-) */
937 nc->be_nodes=L2BE(1);
939 on=(struct fsObjectNode *)nc->be_node;
940 on->node.be_data=L2BE(globals->block_root);
942 on++;
943 if(norecycled==FALSE) {
944 on->node.be_data=L2BE(block_recycled);
945 on->be_hash16=W2BE(hash(".recycled", casesensitive));
947 else {
948 on->node.be_data=-1; // reserved 2
951 on++;
952 on->node.be_data=-1; // reserved 3
954 on++;
955 on->node.be_data=-1; // reserved 4
957 on++;
958 on->node.be_data=-1; // reserved 5
960 on++;
961 on->node.be_data=-1; // reserved 6
963 setchecksum(cb);
964 errorcode=writecachebuffer(cb);
967 if(errorcode==0 && norecycled==FALSE) {
968 struct fsObjectContainer *oc=cb->data;
970 _DEBUG(("ACTION_FORMAT: Creating Root ObjectContainer block\n"));
972 cb->blckno=block_recycled;
973 clearcachebuffer(cb);
975 oc->bheader.id=OBJECTCONTAINER_ID;
976 oc->bheader.be_ownblock=L2BE(block_recycled);
977 oc->be_parent=L2BE(ROOTNODE);
978 oc->object[0].be_protection=L2BE(FIBF_READ|FIBF_WRITE);
979 oc->object[0].be_datemodified=L2BE(currentdate);
980 oc->object[0].bits=OTYPE_DIR|OTYPE_UNDELETABLE|OTYPE_QUICKDIR;
981 if(showrecycled==FALSE) {
982 oc->object[0].bits|=OTYPE_HIDDEN;
985 oc->object[0].be_objectnode=L2BE(RECYCLEDNODE);
987 /* NOT HASHED RECYCLED
988 oc->object[0].object.dir.hashtable=block_recycled+1;
990 oc->object[0].object.dir.be_hashtable=0;
992 copystr(".recycled",oc->object[0].name,30);
994 setchecksum(cb);
995 errorcode=writecachebuffer(cb);
998 /* NOT HASHED RECYCLED
999 if(errorcode==0 && norecycled==FALSE) {
1000 struct fsHashTable *ht=cb->data;
1002 _DEBUG(("ACTION_FORMAT: Creating Recycled's HashTable block\n"));
1004 cb->blckno=block_recycled+1;
1005 clearcachebuffer(cb);
1007 ht->bheader.id=HASHTABLE_ID;
1008 ht->bheader.ownblock=block_recycled+1;
1009 ht->parent=RECYCLEDNODE;
1011 setchecksum(cb);
1012 errorcode=writecachebuffer(cb);
1016 if(errorcode==0) {
1017 struct fsBitmap *bm;
1018 UWORD cnt,cnt2;
1019 ULONG block=globals->block_bitmapbase;
1020 LONG startfree=globals->blocks_admin+globals->blocks_bitmap+globals->blocks_reserved_start;
1021 LONG sizefree;
1023 _DEBUG(("ACTION_FORMAT: Creating the Bitmap blocks\n"));
1025 /* Create Bitmap blocks */
1027 sizefree=globals->blocks_total-startfree-globals->blocks_reserved_end;
1029 cnt=globals->blocks_bitmap;
1030 while(cnt-->0 && errorcode==0) {
1031 clearcachebuffer(cb);
1033 bm=cb->data;
1034 bm->bheader.id=BITMAP_ID;
1035 bm->bheader.be_ownblock=L2BE(block);
1037 for(cnt2=0; cnt2<(globals->blocks_inbitmap>>5); cnt2++) {
1038 if(startfree>0) {
1039 startfree-=32;
1040 if(startfree<0) {
1041 bm->bitmap[cnt2]=AROS_LONG2BE((1<<(-startfree))-1);
1042 sizefree+=startfree;
1045 else if(sizefree>0) {
1046 sizefree-=32;
1047 if(sizefree<0) {
1048 bm->bitmap[cnt2]=AROS_LONG2BE(~((1<<(-sizefree))-1));
1050 else {
1051 bm->bitmap[cnt2]=BITMAPFILL;
1054 else {
1055 break;
1059 cb->blckno=block++;
1061 setchecksum(cb);
1062 errorcode=writecachebuffer(cb);
1066 if(errorcode==0) {
1067 struct fsRootBlock *rb;
1069 _DEBUG(("ACTION_FORMAT: Creating the Root blocks\n"));
1071 /* Create Root blocks */
1073 cb->blckno=0;
1074 clearcachebuffer(cb);
1076 rb=cb->data;
1077 rb->bheader.id=L2BE(DOSTYPE_ID);
1078 rb->bheader.be_ownblock=0;
1080 rb->be_version=W2BE(STRUCTURE_VERSION);
1081 rb->be_sequencenumber=0;
1083 rb->be_datecreated=L2BE(currentdate);
1085 rb->be_firstbyteh = L2BE(globals->byte_low >> 32);
1086 rb->be_firstbyte = L2BE(globals->byte_low);
1087 rb->be_lastbyteh = L2BE(globals->byte_high >> 32);
1088 rb->be_lastbyte = L2BE(globals->byte_high);
1090 rb->be_totalblocks=L2BE(globals->blocks_total);
1091 rb->be_blocksize=L2BE(globals->bytes_block);
1093 rb->be_bitmapbase=L2BE(globals->block_bitmapbase);
1094 rb->be_adminspacecontainer=L2BE(globals->block_adminspace);
1095 rb->be_rootobjectcontainer=L2BE(globals->block_root);
1096 rb->be_extentbnoderoot=L2BE(globals->block_extentbnoderoot);
1097 rb->be_objectnoderoot=L2BE(globals->block_objectnoderoot);
1099 if(casesensitive!=FALSE) {
1100 rb->bits|=ROOTBITS_CASESENSITIVE;
1102 if(norecycled==FALSE) {
1103 rb->bits|=ROOTBITS_RECYCLED;
1106 setchecksum(cb);
1107 if((errorcode=writecachebuffer(cb))==0) {
1108 cb->blckno=globals->blocks_total-1;
1110 _DEBUG(("ACTION_FORMAT: Creating the 2nd Root block\n"));
1112 rb->bheader.be_ownblock=L2BE(globals->blocks_total-1);
1114 setchecksum(cb);
1115 errorcode=writecachebuffer(cb);
1119 flushiocache();
1120 update();
1121 motoroff();
1123 if(errorcode!=0) {
1124 _DEBUG(("ACTION_FORMAT: Exiting with errorcode %ld\n",errorcode));
1126 returnpacket(DOSFALSE, errorcode);
1128 else {
1129 returnpacket(DOSTRUE, 0);
1133 break;
1134 case ACTION_INHIBIT:
1135 _DEBUG(("ACTION_INHIBIT(%ld)\n",globals->packet->dp_Arg1));
1137 /* This function nests. Each call to inhibit the disk should be matched
1138 with one to uninhibit the disk. */
1140 if(globals->packet->dp_Arg1!=DOSFALSE) {
1141 #ifdef STARTDEBUG
1142 dreq("Disk inhibited (nesting = %ld).", globals->inhibitnestcounter);
1143 #endif
1145 if(globals->inhibitnestcounter++==0) { // Inhibited for the first time?
1146 globals->disktype=ID_BUSY; /* Must be put before deinitdisk() Feb 27 1999: Maybe not needed anymore */
1147 deinitdisk();
1150 returnpacket(DOSTRUE,0);
1152 else if(globals->inhibitnestcounter>0 && --globals->inhibitnestcounter==0) {
1154 returnpacket(DOSTRUE, 0); /* Workbench keeps doslist locked, and doesn't send any packets
1155 during that time to this handler. As initdisk() needs to lock
1156 the doslist we MUST return the packet before calling initdisk() */
1157 initdisk();
1159 else {
1160 /* Workbench revokes ACTION_INHIBIT without ever actually having
1161 inhibited the volume. We'll just return the packet and ignore
1162 such requests. */
1164 returnpacket(DOSTRUE,0);
1166 break;
1167 case ACTION_SERIALIZE_DISK:
1168 _DEBUG(("ACTION_SERIALIZE_DISK\n"));
1171 struct CacheBuffer *cb;
1172 LONG errorcode;
1174 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
1175 struct fsObjectContainer *oc=cb->data;
1176 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
1178 oc->object[0].be_datemodified=L2BE(getdate());
1179 ri->be_datecreated=oc->object[0].be_datemodified; // BE-BE copy
1181 setchecksum(cb);
1182 errorcode=writecachebuffer(cb);
1185 if(errorcode!=0) {
1186 returnpacket(DOSFALSE,errorcode);
1188 else {
1189 returnpacket(DOSTRUE,0);
1193 break;
1194 default:
1195 if(handlesimplepackets(globals->packet)==0) {
1197 if(globals->disktype == DOSTYPE_ID) {
1198 switch(globals->packet->dp_Type) {
1199 case ACTION_MAKE_LINK:
1200 _DEBUG(("ACTION_MAKE_LINK\n"));
1203 if(globals->packet->dp_Arg4==LINK_HARD) {
1204 returnpacket(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
1206 /* else if(packet->dp_Arg4!=LINK_SOFT) {
1207 returnpacket(DOSFALSE,ERROR_BAD_NUMBER);
1208 } */ /* Check removed because DOS apparantely defines non-zero as being a Soft link! */
1209 else {
1210 struct ExtFileLock *lock;
1211 LONG errorcode;
1213 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1214 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1216 _DEBUG(("ACTION_MAKE_LINK: Name = '%s', LinkPath = '%s'\n",globals->string,(UBYTE *)globals->packet->dp_Arg3));
1218 if((errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,(UBYTE *)globals->packet->dp_Arg3))!=0) {
1219 returnpacket(DOSFALSE,errorcode);
1221 else {
1222 // freelock(lock);
1223 returnpacket(DOSTRUE,0);
1228 break;
1229 case ACTION_READ_LINK:
1230 _DEBUG(("ACTION_READ_LINK\n"));
1233 struct CacheBuffer *cb;
1234 struct fsObject *o;
1235 struct ExtFileLock *lock;
1236 UBYTE *dest=(UBYTE *)globals->packet->dp_Arg3;
1237 LONG errorcode;
1238 NODE objectnode;
1240 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1242 if(lock==0) {
1243 objectnode=ROOTNODE;
1245 else {
1246 objectnode=lock->objectnode;
1249 if((errorcode=readobject(objectnode, &cb, &o))==0) {
1250 UBYTE *path=(UBYTE *)globals->packet->dp_Arg2, *prefix=path;
1252 _DEBUG(("ACTION_READ_LINK: path = '%s', errorcode = %ld\n",path,errorcode));
1254 errorcode=locateobject2(&path, &cb, &o);
1256 if(errorcode!=ERROR_IS_SOFT_LINK) {
1257 errorcode=ERROR_OBJECT_NOT_FOUND;
1259 else {
1260 struct CacheBuffer *cb2;
1261 UBYTE *p=path;
1263 /* Move on to remainder of path after the link */
1265 while(*path!=0) {
1266 if(*path=='/') {
1267 break;
1269 path++;
1272 _DEBUG(("ACTION_READ_LINK: path = '%s'\n",path));
1274 if((errorcode=readcachebuffercheck(&cb2, BE2L(o->object.file.be_data), SOFTLINK_ID))==0) {
1275 struct fsSoftLink *sl=cb2->data;
1276 LONG length=globals->packet->dp_Arg4;
1277 UBYTE *src=sl->string, ch;
1278 UBYTE *s=globals->string;
1280 while((*s++=*path++)!=0) { // Work-around for bug in ixemul, which sometimes provides the same pointer for dp_Arg2 (path) and dp_Arg3 (soft-link buffer)
1283 s=globals->string;
1285 _DEBUG(("ACTION_READ_LINK: length = %ld, sl->string = '%s', path = '%s'\n", length, sl->string, s));
1287 /* cb is no longer valid at this point. */
1289 /* Check if link target is an absolute path */
1291 while((ch=*src++)!='\0') {
1292 if(ch==':')
1293 break;
1295 src=sl->string;
1297 /* If target is a relative path, put path preceding
1298 link into buffer so that result is relative to
1299 lock passed in */
1301 if(ch!=':') {
1302 while(prefix!=p && length-->0) {
1303 *dest++=*prefix++;
1307 /* Copy link target to buffer */
1309 while(length-->0 && (*dest++=*src++)!=0) {
1311 dest--;
1312 length++;
1314 /* Ensure we don't insert an extraneous slash */
1316 if(length!=globals->packet->dp_Arg4 && *(dest-1)=='/') {
1317 *--dest='\0';
1318 length++;
1320 if(*(dest-1)==':' && *s=='/') {
1321 s++;
1324 /* Append remainder of original path */
1326 while(length-->0 && (*dest++=*s++)!=0) {
1329 if(length<0) {
1330 errorcode=ERROR_LINE_TOO_LONG;
1336 if(errorcode!=0) {
1337 if(errorcode==ERROR_LINE_TOO_LONG) {
1338 returnpacket(-2, errorcode);
1340 else {
1341 returnpacket(-1, errorcode);
1344 else {
1345 returnpacket((LONG)((SIPTR)dest - globals->packet->dp_Arg3 - 1),
1349 break;
1350 case ACTION_CREATE_DIR:
1351 _DEBUG(("ACTION_CREATE_DIR\n"));
1354 struct ExtFileLock *lock;
1355 LONG errorcode;
1357 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1358 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1360 if((errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,0))!=0) {
1361 returnpacket(0,errorcode);
1363 else {
1364 returnpacket((SIPTR)TOBADDR(lock),0);
1368 break;
1369 case ACTION_FINDUPDATE:
1370 case ACTION_FINDINPUT:
1371 case ACTION_FINDOUTPUT:
1372 case ACTION_FH_FROM_LOCK:
1373 _XDEBUG((DEBUG_OBJECTS, "ACTION_FIND#? or ACTION_FH_FROM_LOCK\n"));
1376 struct ExtFileLock *lock;
1377 struct FileHandle *fh;
1378 LONG errorcode=0;
1380 fh=(struct FileHandle *)BADDR(globals->packet->dp_Arg1);
1381 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg2);
1383 if(globals->packet->dp_Type!=ACTION_FH_FROM_LOCK) {
1384 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1385 _DEBUG(("OPEN FILE: %s (mode = %ld)\n", globals->string, globals->packet->dp_Type));
1386 errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,0);
1389 if(errorcode==0) {
1390 if((errorcode=createglobalhandle(lock))==0) {
1391 fh->fh_Arg1=(IPTR)lock;
1393 else if(globals->packet->dp_Type!=ACTION_FH_FROM_LOCK) {
1394 freelock(lock);
1398 if(errorcode!=0) {
1399 returnpacket(DOSFALSE,errorcode);
1401 else {
1402 if(globals->packet->dp_Type==ACTION_FINDOUTPUT && globals->has_recycled!=FALSE) {
1403 ULONG files,blocks;
1405 if((errorcode=getrecycledinfo(&files, &blocks))==0) {
1406 if(files>35) {
1407 cleanupdeletedfiles();
1412 returnpacket(DOSTRUE,0);
1416 break;
1417 case ACTION_END:
1418 _XDEBUG((DEBUG_OBJECTS, "ACTION_END\n"));
1420 /* ACTION_FREE_LOCK's code is similair */
1423 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1424 LONG errorcode;
1426 /* If a file has been modified by the use of ACTION_SET_FILE_SIZE or
1427 ACTION_WRITE, or ACTION_FINDOUTPUT or ACTION_FINDUPDATE (only when
1428 creating a new file) were used to create the file then we must
1429 also update the datestamp of the file. We also must send out a
1430 notification. Finally we also need to clear the A bit.
1432 For this purpose a special flag in the lock tells us
1433 whether or not any of the above actions has occured. */
1435 if((lock->bits & EFL_MODIFIED) != 0) {
1436 struct CacheBuffer *cb;
1437 struct fsObject *o;
1439 /* Aha! */
1441 if(lock->lastextendedblock!=0) {
1442 globals->block_rovingblockptr=lock->lastextendedblock;
1443 if(globals->block_rovingblockptr>=globals->blocks_total) {
1444 globals->block_rovingblockptr=0;
1448 if((errorcode=readobject(lock->objectnode, &cb, &o))==0) {
1449 newtransaction();
1451 errorcode=bumpobject(cb, o);
1452 checknotifyforobject(cb, o, TRUE);
1454 if(errorcode==0) {
1455 endtransaction();
1457 else {
1458 deletetransaction();
1462 /* Ignore any errorcodes -- not really interesting when closing a file... */
1465 if((errorcode=freelock(lock))!=0) {
1466 returnpacket(DOSFALSE,errorcode);
1468 else {
1469 returnpacket(DOSTRUE,0);
1472 break;
1473 case ACTION_DELETE_OBJECT:
1475 LONG errorcode;
1477 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1479 _DEBUG(("ACTION_DELETE_OBJECT(0x%08lx,'%s')\n",BADDR(globals->packet->dp_Arg1),globals->string));
1481 do {
1482 newtransaction();
1484 if((errorcode=deleteobject(BADDR(globals->packet->dp_Arg1), validatepath(globals->string), TRUE))==0) {
1485 endtransaction();
1487 else {
1488 deletetransaction();
1490 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1492 if(errorcode!=0) {
1493 returnpacket(DOSFALSE,errorcode);
1495 else {
1496 ULONG files,blocks;
1498 if((errorcode=getrecycledinfo(&files, &blocks))==0) {
1499 if(files>35) {
1500 cleanupdeletedfiles();
1504 returnpacket(DOSTRUE,0);
1507 break;
1508 case ACTION_RENAME_DISK:
1509 _DEBUG(("ACTION_RENAME_DISK\n"));
1512 struct CacheBuffer *cb;
1513 LONG errorcode;
1515 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
1516 if(AttemptLockDosList(LDF_WRITE|LDF_VOLUMES)!=0) {
1517 struct fsObjectContainer *oc=cb->data;
1518 UBYTE *s;
1519 UBYTE *d;
1520 #ifndef USE_FAST_BSTR
1521 UBYTE len;
1522 #endif
1524 /* Succesfully locked the doslist */
1526 newtransaction();
1528 preparecachebuffer(cb);
1531 s=BADDR(globals->packet->dp_Arg1);
1532 d=oc->object[0].name;
1533 d[copybstrasstr((BSTR)globals->packet->dp_Arg1, d, 30)+1] = 0;
1534 #if 0
1535 len=*s++;
1537 if(len>30) {
1538 len=30;
1541 while(len-->0) {
1542 *d++=*s++;
1544 *d++=0;
1545 *d=0; /* Zero for comment */
1546 #endif
1547 if((errorcode=storecachebuffer(cb))==0 && globals->volumenode!=0) {
1548 s=BADDR(globals->packet->dp_Arg1);
1549 d=BADDR(globals->volumenode->dl_Name);
1550 #ifdef USE_FAST_BSTR
1551 copystr(s, d, 30);
1552 #else
1553 len=*s++;
1555 if(len>30) {
1556 len=30;
1559 *d++=len;
1560 while(len-->0) {
1561 *d++=*s++;
1563 *d=0;
1564 #endif
1567 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
1569 if(errorcode==0) {
1570 endtransaction();
1572 else {
1573 deletetransaction();
1576 else {
1577 /* Doslist locking attempt was unsuccesful. Send this message back to our
1578 port to try it again later. */
1580 PutMsg(msgportpackets,msg);
1581 break;
1585 if(errorcode==0) {
1586 returnpacket(DOSTRUE,0);
1588 else {
1589 returnpacket(DOSFALSE,errorcode);
1592 break;
1593 case ACTION_RENAME_OBJECT:
1594 _DEBUG(("ACTION_RENAME_OBJECT\n"));
1597 LONG errorcode;
1599 do {
1600 struct CacheBuffer *cb;
1601 struct fsObject *o;
1602 struct ExtFileLock *lock;
1603 UBYTE *newname;
1604 UBYTE *s;
1606 lock=BADDR(globals->packet->dp_Arg1);
1607 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1609 if((errorcode=locateobjectfromlock(lock,validatepath(globals->string),&cb,&o))==0) {
1610 copybstrasstr((BSTR)globals->packet->dp_Arg4,globals->string,258);
1612 settemporarylock(BE2L(o->be_objectnode));
1614 newname=validatepath(globals->string);
1615 s=FilePart(newname);
1617 if(*s!=0) {
1618 newtransaction();
1619 if((errorcode=renameobject(cb,o,BADDR(globals->packet->dp_Arg3),newname))==0) {
1620 endtransaction();
1622 else {
1623 deletetransaction();
1626 else {
1627 errorcode=ERROR_INVALID_COMPONENT_NAME;
1630 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1632 cleartemporarylock();
1634 if(errorcode!=0) {
1635 returnpacket(DOSFALSE,errorcode);
1637 else {
1638 returnpacket(DOSTRUE,0);
1641 break;
1642 case ACTION_SET_COMMENT:
1643 _DEBUG(("ACTION_SET_COMMENT\n"));
1645 LONG errorcode;
1647 do {
1648 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1649 copybstrasstr((BSTR)globals->packet->dp_Arg4,globals->string2,258);
1651 newtransaction();
1652 if((errorcode=setcomment(BADDR(globals->packet->dp_Arg2),validatepath(globals->string),globals->string2))!=0) {
1653 deletetransaction();
1655 else {
1656 endtransaction();
1658 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1660 if(errorcode!=0) {
1661 returnpacket(DOSFALSE,errorcode);
1663 else {
1664 returnpacket(DOSTRUE,0);
1667 break;
1668 case ACTION_SET_DATE:
1669 case ACTION_SET_PROTECT:
1670 case ACTION_SET_OWNER:
1671 case ACTION_SFS_SET_OBJECTBITS:
1672 _XDEBUG((DEBUG_OBJECTS, "ACTION_SET_DATE or ACTION_SET_PROTECT or ACTION_SET_OWNER\n"));
1675 LONG errorcode;
1677 do {
1678 struct CacheBuffer *cb;
1679 struct fsObject *o;
1680 struct ExtFileLock *lock;
1682 lock=BADDR(globals->packet->dp_Arg2);
1683 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1685 if((errorcode=locatelockableobject(lock,validatepath(globals->string),&cb,&o))==0) {
1686 NODE objectnode=BE2L(o->be_objectnode);
1688 if(objectnode!=ROOTNODE) {
1690 settemporarylock(objectnode);
1692 newtransaction();
1693 preparecachebuffer(cb);
1695 if(globals->packet->dp_Type==ACTION_SET_DATE) {
1696 checksum_writelong_be(cb->data, &o->be_datemodified, datestamptodate((struct DateStamp *)globals->packet->dp_Arg4));
1698 // o->datemodified=datestamptodate((struct DateStamp *)packet->dp_Arg4);
1700 else if(globals->packet->dp_Type==ACTION_SET_PROTECT) {
1701 checksum_writelong_be(cb->data, &o->be_protection, globals->packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE));
1703 // o->protection=packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
1705 else if(globals->packet->dp_Type==ACTION_SET_OWNER) {
1706 // ULONG *owner=(ULONG *)&o->owneruid;
1708 checksum_writelong_be(cb->data, (ULONG *)&o->be_owneruid, globals->packet->dp_Arg4);
1710 // *owner=packet->dp_Arg4;
1712 else {
1713 o->bits=(globals->packet->dp_Arg4 & (OTYPE_HIDDEN|OTYPE_UNDELETABLE)) | (o->bits & ~(OTYPE_HIDDEN|OTYPE_UNDELETABLE));
1714 setchecksum(cb); // new
1717 if((errorcode=storecachebuffer_nochecksum(cb))==0) {
1718 struct GlobalHandle *gh;
1720 checknotifyforobject(cb,o,TRUE);
1721 endtransaction();
1723 if(globals->packet->dp_Type==ACTION_SET_PROTECT && (gh=findglobalhandle(objectnode))!=0) {
1724 gh->protection=globals->packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
1727 else {
1728 deletetransaction();
1731 else {
1732 errorcode=ERROR_OBJECT_WRONG_TYPE;
1736 /* this 'loop' will only be left if the disk wasn't full, or
1737 cleanupdeletedfiles() couldn't free up any more space. */
1739 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1741 cleartemporarylock();
1743 if(errorcode!=0) {
1744 returnpacket(DOSFALSE,errorcode);
1746 else {
1747 returnpacket(DOSTRUE,0);
1750 break;
1751 case ACTION_SET_FILE_SIZE:
1752 _DEBUG(("ACTION_SET_FILE_SIZE(0x%08lx,0x%08lx,0x%08lx)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1755 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1756 LONG newfilesize;
1757 LONG errorcode;
1759 if(globals->packet->dp_Arg3==OFFSET_BEGINNING) {
1760 newfilesize=globals->packet->dp_Arg2;
1762 else if(globals->packet->dp_Arg3==OFFSET_END) {
1763 newfilesize=lock->gh->size+globals->packet->dp_Arg2;
1765 else if(globals->packet->dp_Arg3==OFFSET_CURRENT) {
1766 newfilesize=lock->offset+globals->packet->dp_Arg2;
1768 else {
1769 returnpacket(-1,ERROR_BAD_NUMBER);
1770 break;
1773 if(newfilesize>=0) {
1774 ULONG data=lock->gh->data;
1776 do {
1777 errorcode=setfilesize(lock,newfilesize);
1778 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1780 if(errorcode!=0) {
1781 lock->gh->data=data;
1784 else {
1785 errorcode=ERROR_SEEK_ERROR;
1788 if(errorcode==0) {
1789 returnpacket(lock->gh->size,0);
1791 else {
1792 returnpacket(-1,errorcode);
1795 break;
1796 case ACTION_SEEK:
1797 _XDEBUG((DEBUG_SEEK,"ACTION_SEEK(0x%08lx,0x%08lx,0x%08lx)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1800 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1801 struct GlobalHandle *gh=lock->gh;
1802 ULONG oldpos=lock->offset;
1803 LONG newpos;
1804 LONG errorcode;
1806 if(globals->packet->dp_Arg3==OFFSET_BEGINNING) {
1807 newpos=globals->packet->dp_Arg2;
1809 else if(globals->packet->dp_Arg3==OFFSET_END) {
1810 newpos=gh->size+globals->packet->dp_Arg2;
1812 else if(globals->packet->dp_Arg3==OFFSET_CURRENT) {
1813 newpos=lock->offset+globals->packet->dp_Arg2;
1815 else {
1816 returnpacket(-1,ERROR_BAD_NUMBER);
1817 break;
1820 if(newpos>=0 && newpos<=gh->size) {
1821 if(newpos!=oldpos) {
1822 errorcode=seek(lock,newpos);
1824 else {
1825 errorcode=0;
1828 else {
1829 errorcode=ERROR_SEEK_ERROR;
1832 if(errorcode==0) {
1833 returnpacket(oldpos,0);
1835 else {
1836 returnpacket(-1,errorcode);
1839 break;
1840 case ACTION_READ:
1841 _XDEBUG((DEBUG_IO,"ACTION_READ(0x%08lx,0x%08lx,%ld)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1844 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1845 struct GlobalHandle *gh=lock->gh;
1846 UBYTE *buffer=(UBYTE *)globals->packet->dp_Arg2;
1847 ULONG bytesleft;
1848 UBYTE *startofbuf=buffer;
1849 LONG errorcode;
1851 bytesleft=globals->packet->dp_Arg3;
1852 if(lock->offset+bytesleft > gh->size) {
1853 bytesleft=gh->size-lock->offset;
1856 if((errorcode=seektocurrent(lock))==0) {
1857 if((gh->protection & FIBF_READ)!=0) {
1858 struct CacheBuffer *extent_cb;
1859 struct fsExtentBNode *ebn;
1861 while(bytesleft>0 && (errorcode=findextentbnode(lock->curextent, &extent_cb, &ebn))==0) {
1862 ULONG bytestoread;
1863 ULONG offsetinblock=lock->extentoffset & globals->mask_block;
1864 BLCK ebn_next=BE2L(ebn->be_next);
1865 UWORD ebn_blocks=BE2W(ebn->be_blocks);
1867 _XDEBUG((DEBUG_IO,"ACTION_READ: buffer = 0x%p bytesleft = %ld, offsetinblock = %ld, ExtentBNode = %ld, ebn->data = %ld, ebn->blocks = %ld\n",
1868 buffer, bytesleft, offsetinblock, lock->curextent, BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
1870 if(offsetinblock!=0 || bytesleft<globals->bytes_block) {
1872 // _XDEBUG((DEBUG_IO,"ACTION_READ: nextextentbnode = %ld, extentnodesize = %ld, en->blocks = %ld\n",nextextentbnode,extentnodesize,(ULONG)ebn->blocks));
1874 bytestoread=globals->bytes_block-offsetinblock;
1876 /* Check if there are more bytes left in the block then we want to read */
1877 if(bytestoread > bytesleft) {
1878 bytestoread=bytesleft;
1881 if((errorcode=readbytes(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, offsetinblock, bytestoread))!=0) {
1882 break;
1885 else {
1887 bytestoread=(((ULONG)ebn_blocks)<<globals->shifts_block) - lock->extentoffset;
1889 /* Check if there are more bytes left in the Extent then we want to read */
1890 if(bytestoread > bytesleft) {
1891 bytestoread=bytesleft & ~globals->mask_block;
1894 if((errorcode=read(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, bytestoread>>globals->shifts_block))!=0) {
1895 break;
1899 seekforward(lock, ebn_blocks, ebn_next, bytestoread);
1901 bytesleft-=bytestoread;
1902 buffer+=bytestoread;
1904 _XDEBUG((DEBUG_IO,"ACTION_READ: bytesleft = %ld, errorcode = %ld\n",bytesleft,errorcode));
1907 else {
1908 errorcode=ERROR_READ_PROTECTED;
1912 _XDEBUG((DEBUG_IO,"ACTION_READ: errorcode = %ld, buffer = %ld, startofbuf = %ld\n",errorcode,buffer,startofbuf));
1914 if(errorcode!=0) {
1915 returnpacket(-1,errorcode);
1917 else {
1918 returnpacket(buffer-startofbuf,0);
1921 break;
1922 case ACTION_WRITE:
1923 _XDEBUG((DEBUG_IO,"ACTION_WRITE(0x%08lx,0x%08lx,%ld)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1926 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1927 ULONG bytestowrite=globals->packet->dp_Arg3;
1928 LONG errorcode=0;
1930 do {
1931 struct GlobalHandle *gh=lock->gh;
1933 /* Save some values in case of an error: */
1935 BLCK curextent=lock->curextent;
1936 ULONG extentoffset=lock->extentoffset;
1937 ULONG offset=lock->offset;
1938 ULONG size=gh->size;
1939 ULONG data=gh->data;
1941 if(bytestowrite!=0) {
1942 newtransaction();
1944 if((errorcode=writetofile(lock, (UBYTE *)globals->packet->dp_Arg2, bytestowrite))==0) {
1945 endtransaction();
1947 else {
1948 lock->curextent=curextent;
1949 lock->extentoffset=extentoffset;
1950 lock->offset=offset;
1951 gh->size=size;
1952 gh->data=data;
1954 deletetransaction();
1957 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1959 if(errorcode!=0) {
1960 _XDEBUG((DEBUG_IO,"ACTION_WRITE returns (-1, %ld)\n",errorcode));
1962 returnpacket(-1,errorcode);
1964 else {
1965 _XDEBUG((DEBUG_IO,"ACTION_WRITE returns (%ld, 0)\n",bytestowrite));
1967 lock->bits|=EFL_MODIFIED;
1968 returnpacket(bytestowrite,0);
1971 break;
1972 case ACTION_FREE_LOCK:
1973 _XDEBUG((DEBUG_LOCK,"ACTION_FREE_LOCK\n"));
1976 LONG errorcode;
1978 if((errorcode=freelock((struct ExtFileLock *)BADDR(globals->packet->dp_Arg1)))!=0) {
1979 returnpacket(DOSFALSE,errorcode);
1980 break;
1982 returnpacket(DOSTRUE,0);
1984 break;
1985 case ACTION_EXAMINE_ALL:
1986 _DEBUG(("ACTION_EXAMINE_ALL\n"));
1989 struct ExtFileLock *lock;
1990 struct ExAllData *ead;
1991 struct ExAllData *prevead=0;
1992 struct ExAllControl *eac;
1993 struct CacheBuffer *cb;
1994 struct fsObject *o;
1995 ULONG eadsize;
1996 ULONG stringsize;
1997 LONG spaceleft;
1998 LONG errorcode;
2000 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2001 ead=(struct ExAllData *)globals->packet->dp_Arg2;
2002 eac=(struct ExAllControl *)globals->packet->dp_Arg5;
2003 spaceleft=globals->packet->dp_Arg3;
2005 eac->eac_Entries=0;
2007 if(lock==0) {
2008 _DEBUG(("ACTION_EXAMINE_ALL: Zero lock was passed in...\n"));
2010 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2011 break;
2014 if(globals->packet->dp_Arg4>ED_OWNER) {
2015 returnpacket(DOSFALSE,ERROR_BAD_NUMBER);
2016 break;
2019 if(eac->eac_LastKey==0) {
2020 if((errorcode=readobject(lock->objectnode,&cb,&o))==0) {
2021 if((o->bits & OTYPE_DIR)!=0) {
2022 if(o->object.dir.be_firstdirblock!=0) {
2023 if((errorcode=readcachebuffercheck(&cb,BE2L(o->object.dir.be_firstdirblock),OBJECTCONTAINER_ID))==0) {
2024 struct fsObjectContainer *oc=cb->data;
2026 o=oc->object;
2027 eac->eac_LastKey=BE2L(o->be_objectnode);
2030 else {
2031 errorcode=ERROR_NO_MORE_ENTRIES;
2034 else {
2035 errorcode=ERROR_OBJECT_WRONG_TYPE;
2039 else {
2040 if((errorcode=readobject(eac->eac_LastKey,&cb,&o))==ERROR_IS_SOFT_LINK) {
2041 errorcode=0;
2045 while(errorcode==0) {
2046 WORD namelength=strlen(o->name);
2047 WORD keepentry;
2049 stringsize=0;
2050 eadsize=0;
2052 switch(globals->packet->dp_Arg4) {
2053 default:
2054 case ED_OWNER:
2055 eadsize += 4; /* ed_OwnedGID, ed_OwnedUID */
2056 case ED_COMMENT:
2057 stringsize+=strlen(o->name+namelength+1)+1;
2058 eadsize += sizeof(UBYTE *); /* ed_Comment */
2059 case ED_DATE:
2060 eadsize += 12; /* ed_Ticks, ed_Mins, ed_Days */
2061 case ED_PROTECTION:
2062 eadsize += 4; /* ed_Prot */
2063 case ED_SIZE:
2064 eadsize += 4; /* ed_Size */
2065 case ED_TYPE:
2066 eadsize += 4;
2067 case ED_NAME:
2068 stringsize += namelength+1;
2069 eadsize += sizeof(APTR) * 2; /* ed_Name, ed_Next */
2070 break;
2073 // _DEBUG(("ACTION_EXAMINE_ALL: eadsize = %ld, stringsize = %ld, spaceleft = %ld, packet->dp_Arg4 = %ld\n",eadsize,stringsize,spaceleft,packet->dp_Arg4));
2075 if(spaceleft<eadsize+stringsize) {
2076 break;
2079 switch(globals->packet->dp_Arg4) {
2080 default:
2081 case ED_OWNER:
2082 ead->ed_OwnerUID=BE2W(o->be_owneruid);
2083 ead->ed_OwnerGID=BE2W(o->be_ownergid);
2084 case ED_COMMENT:
2086 UBYTE *src=o->name+namelength+1;
2087 UBYTE *dest=(UBYTE *)ead+eadsize;
2089 ead->ed_Comment=dest;
2091 while(*src!=0) {
2092 *dest++=*src++;
2093 eadsize++;
2096 *dest=0;
2097 eadsize++;
2099 case ED_DATE:
2100 datetodatestamp(BE2L(o->be_datemodified),(struct DateStamp *)&ead->ed_Days);
2101 case ED_PROTECTION:
2102 ead->ed_Prot=BE2L(o->be_protection)^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
2103 case ED_SIZE:
2104 if((o->bits & OTYPE_DIR)==0) {
2105 ead->ed_Size=BE2L(o->object.file.be_size);
2107 else {
2108 ead->ed_Size=0;
2110 case ED_TYPE:
2111 _DEBUG(("examine ED_TYPE, o->bits=%x, o->objectnode=%d\n", o->bits, BE2L(o->be_objectnode)));
2112 if((o->bits & OTYPE_LINK)!=0) {
2113 ead->ed_Type=ST_SOFTLINK;
2115 if((o->bits & OTYPE_DIR)==0) {
2116 ead->ed_Type=ST_FILE;
2118 else if (o->be_objectnode == L2BE(ROOTNODE)) {
2119 ead->ed_Type = ST_ROOT;
2121 else {
2122 ead->ed_Type=ST_USERDIR;
2124 case ED_NAME:
2126 UBYTE *src=o->name;
2127 UBYTE *dest=(UBYTE *)ead+eadsize;
2129 ead->ed_Name=dest;
2131 while(*src!=0) {
2132 *dest++=*src++;
2133 eadsize++;
2136 *dest=0;
2137 eadsize++;
2139 // _DEBUG(("Stored entry %s\n",ead->ed_Name));
2143 if(eac->eac_MatchString!=0) {
2144 keepentry=MatchPatternNoCase(eac->eac_MatchString,ead->ed_Name);
2146 else {
2147 keepentry=DOSTRUE;
2150 if(keepentry!=DOSFALSE && eac->eac_MatchFunc!=0) {
2152 #ifdef __AROS__
2153 keepentry=CALLHOOKPKT(eac->eac_MatchFunc, ead, (APTR)globals->packet->dp_Arg4);
2154 #else
2155 LONG __asm(*hookfunc)(register __a0 struct Hook *,register __a1 struct ExAllData *,register __a2 ULONG)=(LONG __asm(*)(register __a0 struct Hook *,register __a1 struct ExAllData *,register __a2 ULONG))eac->eac_MatchFunc->h_Entry;
2156 keepentry=hookfunc(eac->eac_MatchFunc,ead,packet->dp_Arg4);
2157 #endif
2160 if(keepentry!=DOSFALSE && (o->bits & OTYPE_HIDDEN)==0) {
2161 ead->ed_Next=0;
2162 eadsize = (eadsize + sizeof(APTR) - 1) & ~(sizeof(APTR) - 1);
2163 if(prevead!=0) {
2164 prevead->ed_Next=ead;
2166 prevead=ead;
2167 ead=(struct ExAllData *)((UBYTE *)ead+eadsize);
2168 spaceleft-=eadsize;
2169 eac->eac_Entries++;
2173 struct fsObjectContainer *oc=cb->data;
2174 UBYTE *endadr;
2176 o=nextobject(o);
2178 endadr=(UBYTE *)oc+globals->bytes_block-sizeof(struct fsObject)-2;
2180 if((UBYTE *)o>=endadr || o->name[0]==0) {
2181 if(oc->be_next!=0) {
2182 if((errorcode=readcachebuffercheck(&cb,BE2L(oc->be_next),OBJECTCONTAINER_ID))==0) {
2183 struct fsObjectContainer *oc=cb->data;
2185 o=oc->object;
2186 eac->eac_LastKey=BE2L(o->be_objectnode);
2189 else {
2190 errorcode=ERROR_NO_MORE_ENTRIES;
2193 else {
2194 eac->eac_LastKey=BE2L(o->be_objectnode);
2199 if(errorcode!=0) {
2200 returnpacket(DOSFALSE,errorcode);
2202 else {
2203 returnpacket(DOSTRUE,0);
2206 break;
2207 case ACTION_EXAMINE_NEXT:
2208 // _DEBUG(("ACTION_EXAMINE_NEXT(0x%08lx,0x%08lx)\n",BADDR(packet->dp_Arg1),BADDR(packet->dp_Arg2)));
2210 /* An entry is added to a directory in the first dir block with
2211 enough space to hold the entry. If there is no space, then
2212 a new block is added at the START of the directory, and the
2213 new entry is added there.
2215 In the directory block with enough space, the entry is added
2216 at the end of the block. The order of directory entries there
2217 fore is like this (square parenthesis indicate blocks):
2219 [789] [456] [123]
2221 This means we should scan the directory in the exact opposite
2222 order to avoid a trap when an entry is overwritten during
2223 directory scanning (MODE_NEW_FILE removes old entry, and adds
2224 a new one). See below:
2226 1. [123]; scan is at 1 (next = 2); 1 is overwritten.
2228 2. [231]; scan is at 2 (next = 3); 2 is overwritten.
2230 3. [312]; scan is at 3 (next = 1); 3 is overwritten.
2232 4. [123]; same as 1 -> loop!
2234 By scanning the internals of a dir block backwards the problem
2235 is solved:
2237 1. [123]; scan is at 3 (next = 2); 3 is overwritten.
2239 2. [123]; scan is at 2 (next = 1); 2 is overwritten.
2241 3. [132]; scan is at 1 (next = next block); 1 is overwritten.
2243 4. new block is loaded -> no loop. */
2246 struct ExtFileLock *lock;
2247 struct CacheBuffer *cb;
2248 struct FileInfoBlock *fib=BADDR(globals->packet->dp_Arg2);
2249 LONG errorcode=0;
2251 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2253 if(lock==0) {
2254 _DEBUG(("ACTION_EXAMINE_NEXT: Zero lock was passed in...\n"));
2256 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2257 break;
2261 if(lock->ocblck==0 && lock->ocnode==0) {
2262 _DEBUG(("ACTION_EXAMINE_NEXT: Lock was never passed to EXAMINE_OBJECT or has been re-allocated or the object Examine()d was a file\n"));
2264 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2265 break;
2269 if(lock->currentnode!=0xFFFFFFFF && fib->fib_DiskKey!=lock->currentnode) {
2270 struct fsObject *o;
2272 /* It looks like the lock was reallocated! In this case we must rebuild
2273 the state information in the lock. lock->currentnode, lock->nextnode
2274 and lock->nextnodeblock. */
2276 if((errorcode=readobject(fib->fib_DiskKey, &cb, &o))==0) {
2277 struct fsObjectContainer *oc=cb->data;
2279 lock->currentnode=fib->fib_DiskKey;
2281 if((o=prevobject(o, oc))!=0) {
2282 lock->nextnodeblock=cb->blckno;
2283 lock->nextnode=BE2L(o->be_objectnode);
2285 else {
2286 lock->nextnodeblock=BE2L(oc->be_next);
2287 lock->nextnode=0xFFFFFFFF;
2292 if(lock->nextnodeblock==0) {
2293 errorcode=ERROR_NO_MORE_ENTRIES;
2296 /* The passed in lock describes a directory. EXAMINE_NEXT should return
2297 this directory's entries one at the time. The lock has state information
2298 which helps determine at which entry we currently are. */
2300 while(errorcode==0 && (errorcode=readcachebuffercheck(&cb, lock->nextnodeblock, OBJECTCONTAINER_ID))==0) {
2301 struct fsObjectContainer *oc=cb->data;
2302 struct fsObject *o=0;
2303 UBYTE bits;
2305 if(lock->nextnode!=0xFFFFFFFF) {
2306 /*** It is possible findobject returns 0... */
2307 o=findobject(oc, lock->nextnode);
2310 if(o==0) {
2311 o=lastobject(oc);
2314 bits=o->bits;
2315 fillfib(fib, o);
2316 lock->currentnode=BE2L(o->be_objectnode);
2318 /* prepare for another EXAMINE_NEXT */
2320 /* We need to check if there is another object in this ObjectContainer
2321 following the one we just returned. If there is then return its
2322 node. If there isn't then return the next ObjectContainer ptr and
2323 set ocnode to zero. */
2325 if((o=prevobject(o, oc))!=0) {
2326 /* There IS another object */
2327 lock->nextnode=BE2L(o->be_objectnode);
2329 else {
2330 lock->nextnodeblock=BE2L(oc->be_next);
2331 lock->nextnode=0xFFFFFFFF;
2334 if((bits & OTYPE_HIDDEN)==0) {
2335 break;
2337 else if(lock->nextnodeblock==0) {
2338 errorcode=ERROR_NO_MORE_ENTRIES;
2339 break;
2343 if(errorcode==0) {
2344 returnpacket(DOSTRUE,0);
2346 else {
2347 returnpacket(DOSFALSE,errorcode);
2350 break;
2351 #if 0
2352 /******** OLD EXAMINE_NEXT CODE! */
2353 case ACTION_EXAMINE_NEXT:
2354 // _DEBUG(("ACTION_EXAMINE_NEXT(0x%08lx,0x%08lx)\n",BADDR(packet->dp_Arg1),BADDR(packet->dp_Arg2)));
2356 /* An entry is added to a directory in the first dir block with
2357 enough space to hold the entry. If there is no space, then
2358 a new block is added at the START of the directory, and the
2359 new entry is added there.
2361 In the directory block with enough space, the entry is added
2362 at the end of the block. The order of directory entries there
2363 fore is like this (square parenthesis indicate blocks):
2365 [789] [456] [123]
2367 This means we should scan the directory in the exact opposite
2368 order to avoid a trap when an entry is overwritten during
2369 directory scanning (MODE_NEW_FILE removes old entry, and adds
2370 a new one). See below:
2372 1. [123]; scan is at 1 (next = 2); 1 is overwritten.
2374 2. [231]; scan is at 2 (next = 3); 2 is overwritten.
2376 3. [312]; scan is at 3 (next = 1); 3 is overwritten.
2378 4. [123]; same as 1 -> loop!
2380 By scanning the internals of a dir block backwards the problem
2381 is solved:
2383 1. [123]; scan is at 3 (next = 2); 3 is overwritten.
2385 2. [123]; scan is at 2 (next = 1); 2 is overwritten.
2387 3. [132]; scan is at 1 (next = next block); 1 is overwritten.
2389 4. new block is loaded -> no loop. */
2392 struct ExtFileLock *lock;
2393 struct CacheBuffer *cb;
2394 struct FileInfoBlock *fib=BADDR(packet->dp_Arg2);
2395 LONG errorcode=0;
2397 lock=(struct ExtFileLock *)BADDR(packet->dp_Arg1);
2399 if(lock==0) {
2400 _DEBUG(("ACTION_EXAMINE_NEXT: Zero lock was passed in...\n"));
2402 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2403 break;
2407 if(lock->ocblck==0 && lock->ocnode==0) {
2408 _DEBUG(("ACTION_EXAMINE_NEXT: Lock was never passed to EXAMINE_OBJECT or has been re-allocated or the object Examine()d was a file\n"));
2410 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2411 break;
2415 if(lock->currentnode!=0xFFFFFFFF && fib->fib_DiskKey!=lock->currentnode) {
2416 struct fsObject *o;
2418 /* It looks like the lock was reallocated! In this case we must rebuild
2419 the state information in the lock. lock->currentnode, lock->nextnode
2420 and lock->nextnodeblock. */
2422 if((errorcode=readobject(fib->fib_DiskKey, &cb, &o))==0) {
2423 struct fsObjectContainer *oc=cb->data;
2425 lock->currentnode=fib->fib_DiskKey;
2427 o=nextobject(o);
2429 if(isobject(o,oc)!=FALSE) {
2430 lock->nextnodeblock=cb->blckno;
2431 lock->nextnode=o->objectnode;
2433 else {
2434 lock->nextnodeblock=oc->next;
2435 lock->nextnode=0xFFFFFFFF;
2440 if(lock->nextnodeblock==0) {
2441 errorcode=ERROR_NO_MORE_ENTRIES;
2444 /* The passed in lock describes a directory. EXAMINE_NEXT should return
2445 this directory's entries one at the time. The lock has state information
2446 which helps determine at which entry we currently are. */
2448 while(errorcode==0 && (errorcode=readcachebuffercheck(&cb,lock->nextnodeblock,OBJECTCONTAINER_ID))==0) {
2449 struct fsObjectContainer *oc=cb->data;
2450 struct fsObject *o=0;
2451 UBYTE bits;
2453 if(lock->nextnode!=0xFFFFFFFF) {
2454 /*** It is possible findobject returns 0... */
2455 o=findobject(oc,lock->nextnode);
2457 if(o==0) {
2458 o=oc->object;
2461 bits=o->bits;
2462 fillfib(fib,o);
2463 lock->currentnode=o->objectnode;
2465 /* prepare for another EXAMINE_NEXT */
2467 /* We need to check if there is another object in this ObjectContainer
2468 following the one we just returned. If there is then return its
2469 node. If there isn't then return the next ObjectContainer ptr and
2470 set ocnode to zero. */
2472 o=nextobject(o);
2474 if(isobject(o,oc)!=FALSE) {
2475 /* There IS another object */
2476 lock->nextnode=o->objectnode;
2478 else {
2479 lock->nextnodeblock=oc->next;
2480 lock->nextnode=0xFFFFFFFF;
2483 if((bits & OTYPE_HIDDEN)==0) {
2484 break;
2486 else if(lock->nextnodeblock==0) {
2487 errorcode=ERROR_NO_MORE_ENTRIES;
2488 break;
2492 if(errorcode==0) {
2493 returnpacket(DOSTRUE,0);
2495 else {
2496 returnpacket(DOSFALSE,errorcode);
2499 break;
2500 #endif
2502 case ACTION_EXAMINE_OBJECT:
2503 case ACTION_EXAMINE_FH:
2504 _DEBUG(("ACTION_EXAMINE_OBJECT\n"));
2507 struct ExtFileLock *lock;
2508 struct CacheBuffer *cb;
2509 struct fsObject *o;
2510 NODE objectnode;
2511 LONG errorcode;
2513 if(globals->packet->dp_Type==ACTION_EXAMINE_OBJECT) {
2514 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2516 else {
2517 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2520 if(lock==0) {
2521 objectnode=ROOTNODE;
2523 else {
2524 objectnode=lock->objectnode;
2527 errorcode=readobject(objectnode,&cb,&o);
2528 if(errorcode==0 || errorcode==ERROR_IS_SOFT_LINK) {
2529 fillfib((struct FileInfoBlock *)BADDR(globals->packet->dp_Arg2),o);
2531 /* prepare for EXAMINE_NEXT */
2532 if(lock!=0 && (o->bits & OTYPE_DIR)!=0) {
2533 lock->currentnode=0xFFFFFFFF;
2534 lock->nextnode=0xFFFFFFFF;
2535 lock->nextnodeblock=BE2L(o->object.dir.be_firstdirblock);
2538 returnpacket(DOSTRUE,0);
2540 else {
2541 returnpacket(DOSFALSE,errorcode);
2545 break;
2546 case ACTION_INFO:
2547 _DEBUG(("ACTION_INFO\n"));
2550 struct ExtFileLock *lock=BADDR(globals->packet->dp_Arg1);
2551 struct InfoData *id=BADDR(globals->packet->dp_Arg2);
2553 if(lock!=0 && globals->volumenode!=(struct DeviceList *)BADDR(lock->volume)) {
2554 _DEBUG(("ACTION_INFO: returning error\n"));
2556 returnpacket(DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
2557 break;
2560 fillinfodata(id);
2562 returnpacket(DOSTRUE,0);
2564 break;
2565 case ACTION_MORE_CACHE:
2566 _DEBUG(("ACTION_MORE_CACHE\n"));
2569 LONG errorcode;
2571 errorcode=addcachebuffers(globals->packet->dp_Arg1);
2572 _DEBUG(("ACTION_MORE_CACHE: dp_Arg1 = %ld, totalbuffers = %ld, errorcode = %ld\n",globals->packet->dp_Arg1,globals->totalbuffers,errorcode));
2574 if(errorcode==0) {
2575 // returnpacket(DOSTRUE,totalbuffers);
2577 returnpacket(globals->totalbuffers,0);
2579 else {
2580 returnpacket(DOSFALSE,errorcode);
2583 break;
2584 case ACTION_CHANGE_MODE:
2586 struct ExtFileLock *lock=0;
2587 LONG errorcode=0;
2589 if(globals->packet->dp_Arg1==CHANGE_FH) {
2590 lock=(struct ExtFileLock *)((struct FileHandle *)(BADDR(globals->packet->dp_Arg2)))->fh_Arg1;
2592 else if(globals->packet->dp_Arg1==CHANGE_LOCK) {
2593 lock=BADDR(globals->packet->dp_Arg2);
2596 if(lock!=0) {
2597 if(lock->access!=globals->packet->dp_Arg3) {
2598 if(lock->access!=EXCLUSIVE_LOCK) {
2599 /* Convert shared lock into an exclusive lock. We need to check
2600 if there are no locks besides this one, and that we aren't
2601 trying to get an exclusive lock on the root (which is never
2602 allowed). */
2604 if(lock->objectnode!=ROOTNODE) {
2605 NODE objectnode=lock->objectnode;
2607 lock->objectnode=0; /* this makes sure that our lock is not taken into account by lockable() */
2609 if(lockable(objectnode,EXCLUSIVE_LOCK)!=DOSFALSE) {
2610 /* Lockable says it is possible to lock it exclusively! */
2612 lock->access=EXCLUSIVE_LOCK;
2614 else {
2615 errorcode=ERROR_OBJECT_IN_USE;
2618 lock->objectnode=objectnode;
2620 else {
2621 errorcode=ERROR_OBJECT_IN_USE;
2624 else {
2625 /* Convert exclusive lock into a shared lock. Should always be
2626 possible. */
2628 lock->access=SHARED_LOCK;
2632 else {
2633 errorcode=ERROR_OBJECT_WRONG_TYPE;
2636 if(errorcode==0) {
2637 returnpacket(DOSTRUE,0);
2639 else {
2640 returnpacket(DOSFALSE,errorcode);
2643 break;
2644 case ACTION_PARENT:
2645 case ACTION_PARENT_FH:
2646 _DEBUG(("ACTION_PARENT\n"));
2649 LONG errorcode;
2650 struct ExtFileLock *lock;
2652 if(globals->packet->dp_Type==ACTION_PARENT) {
2653 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2655 else {
2656 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2659 if(lock==0 || lock->objectnode==ROOTNODE) {
2660 returnpacket(0,0);
2661 break;
2664 globals->string[0]='/';
2665 globals->string[1]=0;
2666 if((errorcode=lockobject(lock,globals->string,SHARED_LOCK,&lock))!=0) {
2667 returnpacket(0,errorcode);
2669 else {
2670 returnpacket((SIPTR)TOBADDR(lock),0);
2673 break;
2674 case ACTION_COPY_DIR:
2675 case ACTION_COPY_DIR_FH:
2676 _DEBUG(("ACTION_COPY_DIR\n"));
2679 LONG errorcode;
2680 struct ExtFileLock *lock;
2682 if(globals->packet->dp_Type==ACTION_COPY_DIR) {
2683 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2685 else {
2686 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2689 _DEBUG(("ACTION_COPY_DIR: lock=%p\n", lock));
2691 if((errorcode=lockobject(lock,"",SHARED_LOCK,&lock))!=0) {
2692 _DEBUG(("ACTION_COPY_DIR: Failed to obtain lock!\n"));
2693 returnpacket(0,errorcode);
2695 else {
2696 returnpacket((SIPTR)TOBADDR(lock),0);
2699 break;
2700 case ACTION_LOCATE_OBJECT:
2702 struct ExtFileLock *lock;
2703 LONG errorcode;
2705 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
2707 _XDEBUG((DEBUG_LOCK,"ACTION_LOCATE_OBJECT(0x%08lx,'%s',0x%08lx)\n",BADDR(globals->packet->dp_Arg1),globals->string,globals->packet->dp_Arg3));
2709 if((errorcode=lockobject((struct ExtFileLock *)BADDR(globals->packet->dp_Arg1),validatepath(globals->string),globals->packet->dp_Arg3,&lock))!=0) {
2710 returnpacket(0,errorcode);
2712 else {
2713 returnpacket((SIPTR)TOBADDR(lock),0);
2716 break;
2717 case ACTION_SFS_LOCATE_OBJECT:
2719 struct CacheBuffer *cb;
2720 struct fsObject *o;
2721 struct ExtFileLock *lock=0;
2722 LONG errorcode;
2724 if((errorcode=readobject(globals->packet->dp_Arg1, &cb, &o))==0) {
2725 errorcode=lockobject2(o, globals->packet->dp_Arg2, &lock);
2728 if(errorcode!=0) {
2729 returnpacket(0, errorcode);
2731 else {
2732 returnpacket((IPTR)lock, 0);
2735 break;
2736 case ACTION_ADD_NOTIFY:
2738 struct NotifyRequest *nr;
2740 nr=(struct NotifyRequest *)globals->packet->dp_Arg1;
2742 nr->nr_Next = (IPTR)globals->notifyrequests;
2743 nr->nr_Prev = 0;
2744 if (globals->notifyrequests)
2745 globals->notifyrequests->nr_Prev = (IPTR)nr;
2746 globals->notifyrequests = nr;
2748 _DEBUG(("ACTION_ADD_NOTIFY: Starting notification on %s (flags 0x%08lx)\n",nr->nr_FullName,nr->nr_Flags));
2750 if((nr->nr_Flags & NRF_NOTIFY_INITIAL)!=0) {
2751 notify(nr);
2754 returnpacket(DOSTRUE,0);
2756 break;
2757 case ACTION_REMOVE_NOTIFY:
2759 struct NotifyRequest *nr;
2761 nr=(struct NotifyRequest *)globals->packet->dp_Arg1;
2763 _DEBUG(("ACTION_REMOVE_NOTIFY: Removing notification of %s\n",nr->nr_FullName));
2765 if((nr->nr_Flags & NRF_SEND_MESSAGE) != 0) {
2766 /* Removing all outstanding messages form msgport */
2767 while(GetMsg(nr->nr_stuff.nr_Msg.nr_Port)!=0) {
2769 nr->nr_MsgCount=0;
2772 if(nr->nr_Prev)
2773 ((struct NotifyRequest *)nr->nr_Prev)->nr_Next = nr->nr_Next;
2774 else
2775 globals->notifyrequests = (struct NotifyRequest *)nr->nr_Next;
2777 if (nr->nr_Next)
2778 ((struct NotifyRequest *)nr->nr_Next)->nr_Prev = nr->nr_Prev;
2780 nr->nr_Next = 0;
2781 nr->nr_Prev = 0;
2783 returnpacket(DOSTRUE,0);
2785 break;
2786 case ACTION_FLUSH:
2788 LONG errorcode;
2790 if((errorcode=flushcaches())==0) {
2791 returnpacket(DOSTRUE, 0);
2793 else {
2794 returnpacket(DOSFALSE, errorcode);
2797 break;
2798 case ACTION_SFS_READ_BITMAP:
2800 LONG errorcode;
2802 Forbid();
2803 Permit();
2805 errorcode=extractspace((UBYTE *)globals->packet->dp_Arg1, globals->packet->dp_Arg2, globals->packet->dp_Arg3);
2807 returnpacket(errorcode==0 ? DOSTRUE : DOSFALSE, errorcode);
2809 break;
2810 case ACTION_SFS_DEFRAGMENT_INIT:
2812 globals->block_defragptr=2;
2814 returnpacket(DOSTRUE, 0);
2816 break;
2817 case ACTION_SFS_DEFRAGMENT_STEP:
2819 LONG errorcode;
2821 globals->defragmentsteps=(ULONG *)globals->packet->dp_Arg1;
2822 globals->defragmentlongs=globals->packet->dp_Arg2 - 2;
2824 if(globals->defragmentsteps!=0) {
2825 *globals->defragmentsteps=0;
2828 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk before defragmentation\nbecause of error %ld.", "Retry|Cancel", errorcode)==1) {
2831 if(errorcode==0) {
2833 newtransaction();
2835 if((errorcode=step())!=0) {
2836 deletetransaction();
2838 else {
2839 endtransaction();
2841 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk during defragmentation\nbecause of error %ld.", "Retry|Cancel", errorcode)==1) {
2846 if(errorcode==0) {
2847 struct DefragmentStep *ds=(struct DefragmentStep *)globals->packet->dp_Arg1;
2849 while(ds->id!=0) {
2850 if(ds->id==AROS_LONG2BE(MAKE_ID('M','O','V','E')) && ds->length==3) {
2851 updatelocksaftermove(ds->data[1], ds->data[2], ds->data[0]);
2853 ds=(struct DefragmentStep *)((ULONG *)ds + 2 + ds->length);
2856 returnpacket(DOSTRUE, 0);
2858 else {
2859 returnpacket(DOSFALSE, errorcode);
2862 break;
2863 default:
2864 _DEBUG(("ERROR_ACTION_NOT_KNOWN (packettype = %ld)\n",globals->packet->dp_Type));
2865 returnpacket(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
2866 break;
2869 else if(globals->disktype==ID_NO_DISK_PRESENT) {
2870 dumppackets(globals->packet,ERROR_NO_DISK);
2872 else {
2873 dumppackets(globals->packet,ERROR_NOT_A_DOS_DISK);
2876 break;
2879 } while(msg!=0);
2885 static void fillfib(struct FileInfoBlock *fib,struct fsObject *o)
2887 UBYTE *src;
2888 UBYTE *dest;
2889 UBYTE length;
2891 if (o->be_objectnode==L2BE(ROOTNODE)) {
2892 fib->fib_DirEntryType=ST_ROOT;
2893 fib->fib_Size=BE2L(o->object.file.be_size);
2894 fib->fib_NumBlocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1) >> globals->shifts_block;
2895 } else if((o->bits & OTYPE_LINK)!=0) {
2896 fib->fib_DirEntryType=ST_SOFTLINK;
2897 // fib->fib_DirEntryType=ST_USERDIR; // For compatibility with Diavolo 3.4 -> screw it, DOpus fails...
2898 fib->fib_Size=BE2L(o->object.file.be_size);
2899 fib->fib_NumBlocks=0;
2901 else if((o->bits & OTYPE_DIR)==0) {
2902 fib->fib_DirEntryType=ST_FILE;
2903 fib->fib_Size=BE2L(o->object.file.be_size);
2904 fib->fib_NumBlocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1) >> globals->shifts_block;
2906 else {
2907 fib->fib_DirEntryType=ST_USERDIR;
2908 fib->fib_Size=0;
2909 fib->fib_NumBlocks=1;
2911 fib->fib_Protection=BE2L(o->be_protection)^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
2912 fib->fib_EntryType=fib->fib_DirEntryType;
2913 fib->fib_DiskKey=BE2L(o->be_objectnode);
2914 fib->fib_OwnerUID=BE2W(o->be_owneruid);
2915 fib->fib_OwnerGID=BE2W(o->be_ownergid);
2916 datetodatestamp(BE2L(o->be_datemodified),&fib->fib_Date);
2918 src=o->name;
2919 dest = fib->fib_FileName;
2920 dest++;
2921 length=0;
2923 while(*src!=0) {
2924 *dest++=*src++;
2925 length++;
2927 fib->fib_FileName[0]=length;
2929 src++; /* comment follows name, so just skip the null-byte seperating them */
2931 dest = fib->fib_Comment;
2932 dest++;
2933 length=0;
2935 while(*src!=0) {
2936 *dest++=*src++;
2937 length++;
2939 fib->fib_Comment[0]=length;
2944 static struct DosPacket *getpacket(struct Process *p) {
2945 struct MsgPort *port=&p->pr_MsgPort; /* get port of our process */
2946 struct Message *msg;
2948 if((msg=GetMsg(port))!=0) {
2949 return((struct DosPacket *)msg->mn_Node.ln_Name);
2951 else {
2952 return(0);
2958 static struct DosPacket *waitpacket(struct Process *p) {
2959 struct MsgPort *port=&p->pr_MsgPort; /* get port of our process */
2960 struct Message *msg;
2962 WaitPort(port);
2964 msg=GetMsg(port);
2966 return((struct DosPacket *)msg->mn_Node.ln_Name);
2971 static void returnpacket(SIPTR res1,LONG res2) {
2972 struct Message *msg;
2973 struct MsgPort *replyport;
2975 globals->packet->dp_Res1=res1; /* set return codes */
2976 globals->packet->dp_Res2=res2;
2978 replyport=globals->packet->dp_Port; /* Get ReplyPort */
2980 msg=globals->packet->dp_Link; /* Pointer to the Exec-Message of the packet */
2982 globals->packet->dp_Port=&globals->mytask->pr_MsgPort; /* Setting Packet-Port back */
2984 msg->mn_Node.ln_Name=(char *)globals->packet; /* Connect Message and Packet */
2985 msg->mn_Node.ln_Succ=NULL;
2986 msg->mn_Node.ln_Pred=NULL;
2988 PutMsg(replyport,msg); /* Send the Message */
2993 static void returnpacket2(struct DosPacket *packet, SIPTR res1, LONG res2)
2995 struct Message *msg;
2996 struct MsgPort *replyport;
2998 D(bug("[SFS] Replying, results are %ld/%ld\n", res1, res2));
3000 packet->dp_Res1=res1; /* set return codes */
3001 packet->dp_Res2=res2;
3003 replyport=packet->dp_Port; /* Get ReplyPort */
3005 msg=packet->dp_Link; /* Pointer to the Exec-Message of the packet */
3007 packet->dp_Port=&globals->mytask->pr_MsgPort; /* Setting Packet-Port back */
3009 msg->mn_Node.ln_Name=(char *)packet; /* Connect Message and Packet */
3010 msg->mn_Node.ln_Succ=NULL;
3011 msg->mn_Node.ln_Pred=NULL;
3013 PutMsg(replyport,msg); /* Send the Message */
3018 void starttimeout() {
3019 /* From the AbortIO AutoDocs:
3020 iORequest - pointer to an I/O request block (must have been used
3021 at least once. May be active or finished). */
3023 if(globals->pendingchanges==FALSE) {
3024 globals->inactivitytimer_ioreq->tr_time.tv_secs=globals->inactivity_timeout/2;
3025 globals->inactivitytimer_ioreq->tr_time.tv_micro=(globals->inactivity_timeout*500000)%1000000;
3026 globals->inactivitytimer_ioreq->tr_node.io_Command=TR_ADDREQUEST;
3028 SendIO(&globals->inactivitytimer_ioreq->tr_node);
3030 globals->pendingchanges=TRUE;
3031 globals->timerreset=FALSE;
3033 else {
3034 globals->timerreset=TRUE; /* Indicates that during the timeout there was another request. */
3037 if(globals->activitytimeractive==FALSE) {
3038 globals->activitytimer_ioreq->tr_time.tv_secs=globals->activity_timeout;
3039 globals->activitytimer_ioreq->tr_time.tv_micro=0;
3040 globals->activitytimer_ioreq->tr_node.io_Command=TR_ADDREQUEST;
3042 SendIO(&globals->activitytimer_ioreq->tr_node);
3044 globals->activitytimeractive=TRUE;
3050 void stoptimeout(void) {
3052 if(globals->pendingchanges!=FALSE) {
3053 AbortIO(&globals->inactivitytimer_ioreq->tr_node);
3054 WaitIO(&globals->inactivitytimer_ioreq->tr_node);
3056 globals->pendingchanges=FALSE;
3057 globals->timerreset=FALSE;
3060 // if(activitytimeractive!=FALSE) {
3061 // AbortIO(&activitytimer_ioreq->tr_node);
3062 // WaitIO(&activitytimer_ioreq->tr_node);
3064 // activitytimeractive=FALSE;
3065 // }
3070 LONG flushcaches() {
3071 LONG errorcode;
3073 /* Flushes any pending changes to disk and ask the user what to do when
3074 an error occurs. */
3076 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk because of error %ld.", "Retry|Cancel", errorcode)==1) {
3079 motoroff();
3081 return(errorcode);
3086 void invalidatecaches() {
3088 /* Invalidates all caches without flushing. Call flushcaches()
3089 first. */
3091 invalidatecachebuffers();
3092 invalidateiocaches();
3097 LONG readroots(void)
3099 struct CacheBuffer *cb1;
3100 struct CacheBuffer *cb2;
3101 struct fsRootBlock *rb1;
3102 struct fsRootBlock *rb2;
3103 WORD rb1okay=TRUE;
3104 WORD rb2okay=TRUE;
3105 UQUAD first, last;
3106 LONG errorcode;
3108 if((errorcode=readcachebuffer(&cb1,0))!=0) {
3109 return(errorcode);
3112 lockcachebuffer(cb1);
3114 if((errorcode=readcachebuffer(&cb2,globals->blocks_total-1))!=0) {
3115 unlockcachebuffer(cb1);
3116 return(errorcode);
3119 unlockcachebuffer(cb1);
3121 rb1=cb1->data;
3122 rb2=cb2->data;
3124 if(checkchecksum(cb1)==DOSFALSE || rb1->bheader.id!=L2BE(DOSTYPE_ID) || rb1->bheader.be_ownblock!=0) {
3125 _DEBUG(("cb1/rb1 not ok!\n"));
3126 rb1okay=FALSE;
3129 _DEBUG(("checkchecksum(cb1)=%d, rb1->bheader.id=%08x (wanted %08x), rb1->bheader.ownblock=%d\n",
3130 checkchecksum(cb1),BE2L(rb1->bheader.id), DOSTYPE_ID, BE2L(rb1->bheader.be_ownblock)
3133 if(checkchecksum(cb2)==DOSFALSE || rb2->bheader.id!=L2BE(DOSTYPE_ID) || BE2L(rb2->bheader.be_ownblock)!=BE2L(rb2->be_totalblocks)-1) {
3134 _DEBUG(("cb2/rb2 not ok!\n"));
3135 rb2okay=FALSE;
3138 _DEBUG(("checkchecksum(cb2)=%d, rb2->bheader.id=%08x, rb2->bheader.ownblock=%d, rb2->be_totalblocks =%d\n",
3139 checkchecksum(cb2),BE2L(rb2->bheader.id), BE2L(rb2->bheader.be_ownblock), BE2L(rb2->be_totalblocks)
3142 if(rb1okay!=FALSE && rb2okay!=FALSE) {
3143 /* Both root blocks look okay. */
3146 if(rb1->sequencenumber!=rb2->sequencenumber) {
3147 // Sequence numbers differ!
3151 /* Check sizes stored in rootblock */
3152 if ((rb1->be_blocksize != L2BE(globals->bytes_block)) || (rb1->be_totalblocks!=L2BE(globals->blocks_total)))
3154 _DEBUG(("bad size in rb1!\n"));
3155 return(ERROR_NOT_A_DOS_DISK);
3159 * Historically SFS rootblock holds absolute start and end positions on the disk in bytes.
3160 * They are used for validation and nothing else.
3161 * However, a situation is possible when for example someone takes an image of SFS partition
3162 * and then tries to mount it.
3163 * In order to make it working we compare lengths, not positions. If length is okay, the rootblock
3164 * is assumed to be okay.
3166 first = ((UQUAD)BE2L(rb1->be_firstbyteh) << 32) | BE2L(rb1->be_firstbyte);
3167 last = ((UQUAD)BE2L(rb1->be_lastbyteh) << 32) | BE2L(rb1->be_lastbyte);
3169 if (last - first != globals->byte_high - globals->byte_low)
3171 _DEBUG(("bad value in rb1!\n"));
3172 return ERROR_NOT_A_DOS_DISK;
3175 if(rb1->be_version!=BE2W(STRUCTURE_VERSION)) {
3176 /* Different version! */
3178 request(PROGRAMNAME " request","%s\n"\
3179 "is in a format unsupported by this version\n"\
3180 "of the filesystem. Please reformat the disk or\n"\
3181 "install the correct version of this filesystem.",
3182 "Ok",AROS_BSTR_ADDR(globals->devnode->dn_Name));
3184 return(ERROR_NOT_A_DOS_DISK);
3187 globals->block_bitmapbase=BE2L(rb1->be_bitmapbase);
3188 globals->block_adminspace=BE2L(rb1->be_adminspacecontainer);
3189 globals->block_root=BE2L(rb1->be_rootobjectcontainer);
3190 globals->block_extentbnoderoot=BE2L(rb1->be_extentbnoderoot);
3191 globals->block_objectnoderoot=BE2L(rb1->be_objectnoderoot);
3193 if((rb1->bits & ROOTBITS_CASESENSITIVE)!=0) {
3194 globals->is_casesensitive=TRUE;
3196 else {
3197 globals->is_casesensitive=FALSE;
3200 if((rb1->bits & ROOTBITS_RECYCLED)!=0) {
3201 globals->has_recycled=TRUE;
3203 else {
3204 globals->has_recycled=FALSE;
3207 else {
3208 errorcode=ERROR_NOT_A_DOS_DISK;
3211 return(errorcode);
3216 struct DeviceList *usevolumenode(UBYTE *name, ULONG creationdate) {
3217 struct DosList *dol;
3218 struct DeviceList *vn=0;
3220 /* This function locates the specified volumenode, and if found
3221 uses it for the current volume inserted. If the node is not
3222 found this function returns 0. If the specified volumenode
3223 is found, but is found to be in use, then this function
3224 returns -1. Otherwise the found volumenode is returned. */
3226 dol=LockDosList(LDF_READ|LDF_VOLUMES);
3228 while((dol=FindDosEntry(dol, name, LDF_VOLUMES))!=0) {
3229 if(datestamptodate(&dol->dol_misc.dol_volume.dol_VolumeDate)==creationdate) { // Do volumes have same creation date?
3230 Forbid();
3231 if(dol->dol_misc.dol_volume.dol_LockList!=0 || ((struct DeviceList *)dol)->dl_unused!=0) { // Is volume not in use?
3232 struct NotifyRequest *nr;
3233 struct ExtFileLock *lock;
3235 /* Volume is not currently in use, so grab locklist & notifyrequests and patch fl_Task fields */
3237 _DEBUG(("usevolumenode: Found DosEntry with same date, and locklist!=0\n"));
3239 lock=(struct ExtFileLock *)BADDR(dol->dol_misc.dol_volume.dol_LockList);
3240 nr=(struct NotifyRequest *)BADDR((((struct DeviceList *)dol)->dl_unused));
3241 dol->dol_misc.dol_volume.dol_LockList=0;
3242 ((struct DeviceList *)dol)->dl_unused=0;
3244 Permit();
3246 globals->locklist=lock;
3247 globals->notifyrequests=nr;
3249 while(lock!=0) {
3250 lock->task=&globals->mytask->pr_MsgPort;
3251 lock=lock->next;
3254 while(nr!=0) {
3255 nr->nr_Handler=&globals->mytask->pr_MsgPort;
3256 nr = (struct NotifyRequest *)nr->nr_Next;
3259 vn=(struct DeviceList *)dol;
3260 break;
3262 else {
3263 Permit();
3265 _DEBUG(("usevolumenode: Found DosEntry with same date, but it is in use!\n"));
3267 vn=(struct DeviceList *)-1;
3271 /* Volume nodes are of different date, continue search */
3272 dol=NextDosEntry(dol, LDF_VOLUMES);
3275 UnLockDosList(LDF_READ|LDF_VOLUMES);
3277 return(vn);
3282 LONG initdisk() {
3284 /* This routine is called whenever a disk has changed (via ACTION_INHIBIT or
3285 on startup). The routine scans the disk and initializes the necessary
3286 variables. Steps:
3288 - Check if the disk is a valid DOS disk
3289 If so, get name and datestamp of the disk
3290 If valid, look for volume node of the same name and datestamp
3291 If so, retrieve locklist from dol_LockList (fix fl_Task fields!)
3292 and use existing volume node.
3293 If not, a new volume node is created.
3294 If the disk carries the same attributes as another ACTIVE volume
3295 the handler ignores the newly inserted volume.
3296 If not valid, R/W error requester -> "NDOS"
3297 If not valid type is "NDOS"
3301 changegeometry(globals->dosenvec);
3303 if(globals->volumenode==0) {
3304 struct CacheBuffer *cb;
3305 struct fsObjectContainer *oc=0;
3306 ULONG newdisktype;
3307 LONG errorcode;
3309 globals->diskstate=writeprotection();
3311 if((errorcode=readroots())==0) {
3313 /* Root blocks are valid for this filesystem */
3315 _DEBUG(("Initdisk: Root blocks read\n"));
3317 #ifdef STARTDEBUG
3318 dreq("Root blocks are okay!");
3319 #endif
3321 if((errorcode=checkfortransaction())==0) {
3323 _DEBUG(("Initdisk: Checked for an old Transaction and applied it if it was present.\n"));
3325 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
3326 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
3327 ULONG blocksfree=0;
3329 lockcachebuffer(cb);
3330 oc=cb->data;
3332 _DEBUG(("Initdisk: '%s' was inserted\n",oc->object[0].name));
3334 /* ROOT block is valid for this filesystem */
3336 /* We should count the number of set bits in the bitmap now */
3339 struct CacheBuffer *cb;
3340 struct fsBitmap *b;
3341 BLCK bitmapblock=globals->block_bitmapbase;
3342 UWORD cnt=globals->blocks_bitmap;
3343 WORD n;
3345 while(cnt-->0 && (errorcode=readcachebuffercheck(&cb,bitmapblock,BITMAP_ID))==0) {
3346 b=cb->data;
3348 for(n=0; n<((globals->bytes_block-sizeof(struct fsBitmap))>>2); n++) {
3349 if(b->bitmap[n]!=0) {
3350 if(b->bitmap[n]==0xFFFFFFFF) {
3351 blocksfree+=32;
3353 else {
3354 blocksfree+=bfcnto(b->bitmap[n]);
3358 bitmapblock++;
3362 unlockcachebuffer(cb);
3364 _DEBUG(("Initdisk: Traversed bitmap, found %ld free blocks\n",blocksfree));
3366 if(errorcode==0 && BE2L(ri->be_freeblocks)!=blocksfree) {
3367 if(ri->be_freeblocks!=0) {
3368 dreq("The number of free blocks (%ld) is incorrect.\n"\
3369 "According to the bitmap it should be %ld.\n"\
3370 "The number of free blocks will now be updated.", BE2L(ri->be_freeblocks), blocksfree);
3373 newtransaction();
3375 preparecachebuffer(cb);
3377 ri->be_freeblocks=L2BE(blocksfree);
3379 if((errorcode=storecachebuffer(cb))==0) {
3380 endtransaction();
3382 else {
3383 deletetransaction();
3387 if(errorcode==0) {
3388 _DEBUG(("Initdisk: A valid DOS disk\n"));
3390 newdisktype = DOSTYPE_ID;
3392 #ifdef STARTDEBUG
3393 dreq("There is a valid SFS disk present!");
3394 #endif
3396 else {
3398 #ifdef STARTDEBUG
3399 dreq("SFS disk is invalid; bitmap error.");
3400 #endif
3402 newdisktype=ID_NOT_REALLY_DOS;
3403 errorcode=ERROR_NOT_A_DOS_DISK;
3406 else {
3408 #ifdef STARTDEBUG
3409 dreq("SFS disk is invalid; root objectcontainer error.");
3410 #endif
3412 newdisktype=ID_NOT_REALLY_DOS;
3413 errorcode=ERROR_NOT_A_DOS_DISK;
3416 else {
3418 #ifdef STARTDEBUG
3419 dreq("SFS disk is invalid; transaction error.");
3420 #endif
3422 newdisktype=ID_NOT_REALLY_DOS;
3423 errorcode=ERROR_NOT_A_DOS_DISK;
3426 else if(errorcode==INTERR_CHECKSUM_FAILURE || errorcode==ERROR_NOT_A_DOS_DISK) {
3427 newdisktype=ID_NOT_REALLY_DOS;
3428 errorcode=ERROR_NOT_A_DOS_DISK;
3430 #ifdef STARTDEBUG
3431 dreq("SFS disk is invalid; checksum failure.");
3432 #endif
3434 else if(errorcode==INTERR_BLOCK_WRONG_TYPE) {
3435 #ifdef STARTDEBUG
3436 dreq("SFS disk is invalid; wrong block type.");
3437 #endif
3439 newdisktype=ID_NOT_REALLY_DOS;
3440 errorcode=ERROR_NOT_A_DOS_DISK;
3442 else if(errorcode==TDERR_DiskChanged) {
3443 #ifdef STARTDEBUG
3444 dreq("SFS disk is invalid; disk was changed.");
3445 #endif
3447 newdisktype=ID_NO_DISK_PRESENT;
3448 errorcode=ERROR_NO_DISK;
3450 else {
3451 #ifdef STARTDEBUG
3452 dreq("SFS disk is invalid; unreadable disk.");
3453 #endif
3455 newdisktype=ID_UNREADABLE_DISK;
3456 errorcode=ERROR_NOT_A_DOS_DISK;
3459 if(errorcode==0) {
3460 struct DeviceList *vn;
3461 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)oc+globals->bytes_block-sizeof(struct fsRootInfo));
3463 _DEBUG(("initdisk: Checking for an existing volume-node\n"));
3465 if((vn=usevolumenode(oc->object[0].name, BE2L(ri->be_datecreated)))!=(struct DeviceList *)-1) {
3466 if(vn==0) {
3467 /* VolumeNode was not found, so we need to create a new one. */
3469 _DEBUG(("initdisk: No volume-node found, creating new one instead.\n"));
3471 if((vn=(struct DeviceList *)MakeDosEntry(" ",DLT_VOLUME))!=0) {
3472 struct SFSMessage *sfsm;
3473 UBYTE *d2=(UBYTE *)BADDR(vn->dl_Name);
3474 #ifdef AROS_FAST_BSTR
3475 copystr(oc->object[0].name, d2, 30);
3476 #else
3477 UBYTE *d=d2+1;
3478 UBYTE *s=oc->object[0].name;
3479 UBYTE len=0;
3481 while(*s!=0 && len<30) {
3482 *d++=*s++;
3483 len++;
3486 *d=0;
3487 *d2=len;
3488 #endif
3490 datetodatestamp(BE2L(ri->be_datecreated), &vn->dl_VolumeDate);
3492 _DEBUG(("initdisk: Sending msg.\n"));
3494 if((sfsm=AllocVec(sizeof(struct SFSMessage), MEMF_CLEAR))!=0) {
3495 sfsm->command=SFSM_ADD_VOLUMENODE;
3496 sfsm->data=(IPTR)vn;
3497 sfsm->msg.mn_Length=sizeof(struct SFSMessage);
3499 PutMsg(globals->sdlhport, (struct Message *)sfsm);
3502 else {
3503 errorcode=ERROR_NO_FREE_STORE;
3507 _DEBUG(("initdisk: Using new or old volumenode.\n"));
3509 if(errorcode==0) { /* Reusing the found VolumeNode or using the new VolumeNode */
3510 vn->dl_Task=globals->devnode->dn_Task;
3511 vn->dl_DiskType=globals->dosenvec->de_DosType;
3512 globals->volumenode=vn;
3515 else { /* Volume is in use by another handler -- stay off */
3516 _DEBUG(("Initdisk: Found DosEntry with same date, and locklist==0\n"));
3517 newdisktype=ID_NO_DISK_PRESENT; /* Hmmm... EXTREMELY unlikely, but may explain the strange bug Laire had. */
3518 errorcode=ERROR_NO_DISK;
3519 // vn=0;
3520 globals->volumenode=0;
3524 if(errorcode==0) {
3525 diskchangenotify(IECLASS_DISKINSERTED);
3528 globals->disktype=newdisktype;
3530 return(errorcode);
3532 else {
3533 return(0);
3539 static struct DosList *attemptlockdoslist(LONG tries, LONG delay) {
3540 struct DosList *dol;
3541 struct DosPacket *dp;
3543 for(;;) {
3544 dol=AttemptLockDosList(LDF_WRITE|LDF_VOLUMES);
3546 if(((IPTR)dol & ~1)!=0) {
3547 return(dol);
3550 if(--tries<=0) {
3551 return(0);
3554 if((dp=getpacket(globals->mytask))!=0) {
3555 if(handlesimplepackets(dp)==0) {
3556 dumppackets(dp, ERROR_NOT_A_DOS_DISK);
3559 else {
3560 Delay(delay);
3566 void removevolumenode(struct DosList *dol, struct DosList *vn) {
3567 while((dol=NextDosEntry(dol, LDF_VOLUMES))!=0) {
3568 if(dol==vn) {
3569 RemDosEntry(dol);
3570 break;
3576 static void deinitdisk() {
3578 /* This function flushes all caches, and then invalidates them.
3579 If successful, this function then proceeds to either remove
3580 the volumenode, or transfer any outstanding locks/notifies to
3581 it. Finally it notifies the system of the disk removal. */
3583 _DEBUG(("deinitdisk: entry\n"));
3585 flushcaches();
3586 invalidatecaches();
3588 if(globals->volumenode!=0) {
3590 /* We first check if the VolumeNode needs to be removed; if not
3591 then we do not lock the DosList and modify some of its fields.
3592 If it must be removed, we first attempt to lock the DosList
3593 synchronously; whether this fails or not, we always send a
3594 removal message to the DosList subtask. */
3596 if(globals->locklist!=0 || globals->notifyrequests!=0) {
3598 /* There are locks or notifyrequests, so we cannot kill the volumenode. */
3600 /* We haven't got the DosList locked here, but I think it is fairly
3601 safe to modify these fields directly. */
3603 Forbid();
3605 globals->volumenode->dl_Task=0;
3606 globals->volumenode->dl_LockList=MKBADDR(globals->locklist);
3607 globals->volumenode->dl_unused=MKBADDR(globals->notifyrequests);
3609 Permit();
3611 globals->locklist=0;
3612 globals->notifyrequests=0;
3614 else {
3615 struct SFSMessage *sfsm;
3616 struct DosList *dol=attemptlockdoslist(5, 1);
3618 _DEBUG(("deinitdisk: dol = %ld\n", dol));
3620 if(dol!=0) { /* Is DosList locked? */
3621 removevolumenode(dol, (struct DosList *)globals->volumenode);
3622 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
3625 _DEBUG(("deinitdisk: sending msg\n"));
3627 /* Even if we succesfully locked the DosList, we still should notify
3628 the DosList task to remove the node as well (and to free it), just
3629 in case a VolumeNode Add was still pending. */
3631 if((sfsm=AllocVec(sizeof(struct SFSMessage), MEMF_CLEAR))!=0) {
3632 sfsm->command=SFSM_REMOVE_VOLUMENODE;
3633 sfsm->data=(IPTR)globals->volumenode;
3634 sfsm->msg.mn_Length=sizeof(struct SFSMessage);
3636 PutMsg(globals->sdlhport, (struct Message *)sfsm);
3640 _DEBUG(("deinitdisk: done\n"));
3642 globals->volumenode=0;
3644 diskchangenotify(IECLASS_DISKREMOVED);
3650 LONG handlesimplepackets(struct DosPacket *packet) {
3651 LONG type=packet->dp_Type; /* After returnpacket, packet->dp_Type is invalid! */
3653 switch(packet->dp_Type) {
3654 case ACTION_IS_FILESYSTEM:
3655 returnpacket2(packet, DOSTRUE,0);
3656 break;
3657 #ifndef __AROS__
3658 case ACTION_GET_DISK_FSSM:
3659 returnpacket2(packet, (LONG)startupmsg,0);
3660 break;
3661 case ACTION_FREE_DISK_FSSM:
3662 returnpacket2(packet, DOSTRUE,0);
3663 break;
3664 #endif
3665 case ACTION_DISK_INFO:
3666 actiondiskinfo(packet);
3667 break;
3668 case ACTION_SAME_LOCK:
3669 actionsamelock(packet);
3670 break;
3671 case ACTION_CURRENT_VOLUME:
3672 actioncurrentvolume(packet);
3673 break;
3674 default:
3675 return(0);
3678 return(type);
3683 static LONG dumppackets(struct DosPacket *packet,LONG returncode) {
3684 LONG type=packet->dp_Type; /* After returnpacket, packet->dp_Type is invalid! */
3686 /* Routine which returns ERROR_NOT_A_DOS_DISK for all known
3687 packets which cannot be handled under such a situation */
3689 switch(type) {
3690 case ACTION_READ:
3691 case ACTION_WRITE:
3692 case ACTION_SEEK:
3693 case ACTION_SET_FILE_SIZE:
3694 returnpacket2(packet, -1,returncode);
3695 break;
3696 case ACTION_CREATE_DIR:
3697 case ACTION_FINDUPDATE:
3698 case ACTION_FINDINPUT:
3699 case ACTION_FINDOUTPUT:
3700 case ACTION_END:
3701 case ACTION_EXAMINE_OBJECT:
3702 case ACTION_EXAMINE_NEXT:
3703 case ACTION_COPY_DIR:
3704 case ACTION_LOCATE_OBJECT:
3705 case ACTION_PARENT: /* till here verified to return 0 on error! */
3706 case ACTION_LOCK_RECORD:
3707 case ACTION_FREE_RECORD:
3708 case ACTION_FREE_LOCK:
3709 case ACTION_CHANGE_MODE:
3710 case ACTION_FH_FROM_LOCK:
3711 case ACTION_COPY_DIR_FH:
3712 case ACTION_PARENT_FH:
3713 case ACTION_EXAMINE_FH:
3714 case ACTION_EXAMINE_ALL:
3715 case ACTION_DELETE_OBJECT:
3716 case ACTION_RENAME_OBJECT:
3717 case ACTION_MAKE_LINK:
3718 case ACTION_READ_LINK:
3719 case ACTION_SET_COMMENT:
3720 case ACTION_SET_DATE:
3721 case ACTION_SET_PROTECT:
3722 case ACTION_INFO:
3723 case ACTION_RENAME_DISK:
3724 case ACTION_SERIALIZE_DISK:
3725 #ifndef __AROS__
3726 case ACTION_GET_DISK_FSSM:
3727 #endif
3728 case ACTION_MORE_CACHE:
3729 case ACTION_WRITE_PROTECT:
3730 case ACTION_ADD_NOTIFY:
3731 case ACTION_REMOVE_NOTIFY:
3732 case ACTION_INHIBIT: /* These packets are only dumped when doslist is locked */
3733 case ACTION_FORMAT:
3734 returnpacket2(packet, DOSFALSE,returncode);
3735 break;
3736 default:
3737 returnpacket2(packet, DOSFALSE,ERROR_ACTION_NOT_KNOWN);
3738 break;
3741 return(type);
3745 #ifdef DEBUGCODE
3746 static void dumppacket() {
3747 struct DosPacket *packet;
3749 /* routine which gets a packet and returns ERROR_NOT_A_DOS_DISK for all
3750 known packets which cannot be executed while we are attempting to access
3751 the doslist. Some packets which don't require disk access will be
3752 handled as normal. */
3754 packet=waitpacket(globals->mytask);
3756 if(handlesimplepackets(packet)==0) {
3757 dumppackets(packet,ERROR_NOT_A_DOS_DISK);
3760 #endif
3763 static void actioncurrentvolume(struct DosPacket *packet) {
3764 struct ExtFileLock *lock=(struct ExtFileLock *)packet->dp_Arg1;
3766 _DEBUG(("ACTION_CURRENT_VOLUME(%ld)\n",lock));
3768 if(lock==0) {
3769 _DEBUG(("ACTION_CURRENT_VOLUME: volumenode = %ld\n",globals->volumenode));
3770 returnpacket2(packet, (SIPTR)TOBADDR(globals->volumenode), 0);
3772 else {
3773 returnpacket2(packet, (SIPTR)lock->volume,0);
3779 static void actionsamelock(struct DosPacket *packet) {
3780 struct ExtFileLock *lock;
3781 struct ExtFileLock *lock2;
3783 lock=(struct ExtFileLock *)BADDR(packet->dp_Arg1);
3784 lock2=(struct ExtFileLock *)BADDR(packet->dp_Arg2);
3786 if(lock->objectnode==lock2->objectnode && lock->task==lock2->task) {
3787 returnpacket2(packet, DOSTRUE,0);
3788 return;
3790 returnpacket2(packet, DOSFALSE,0);
3795 static void actiondiskinfo(struct DosPacket *packet) {
3796 struct InfoData *id=BADDR(packet->dp_Arg1);
3798 _DEBUG(("ACTION_DISK_INFO\n"));
3800 fillinfodata(id);
3802 returnpacket2(packet, DOSTRUE,0);
3807 static void fillinfodata(struct InfoData *id) {
3808 ULONG usedblocks;
3810 id->id_NumSoftErrors=globals->numsofterrors;
3811 id->id_UnitNumber=globals->startupmsg->fssm_Unit;
3812 id->id_DiskState=globals->diskstate;
3813 id->id_NumBlocks=globals->blocks_total-globals->blocks_reserved_start-globals->blocks_reserved_end;
3815 usedblocks=id->id_NumBlocks;
3817 if(globals->disktype == DOSTYPE_ID) {
3818 ULONG deletedfiles, deletedblocks;
3820 getusedblocks(&usedblocks);
3821 if(getrecycledinfo(&deletedfiles, &deletedblocks)==0) {
3822 usedblocks-=deletedblocks;
3826 id->id_NumBlocksUsed=usedblocks;
3827 id->id_BytesPerBlock=globals->bytes_block;
3828 id->id_DiskType=globals->disktype;
3830 D(kprintf("Filling InfoData structure with a volumenode ptr to address %ld. Disktype = 0x%08lx\n", globals->volumenode, globals->disktype));
3832 id->id_VolumeNode=TOBADDR(globals->volumenode);
3834 _DEBUG(("fillinfodata: volumenode = %ld, disktype = 0x%08lx\n", globals->volumenode, globals->disktype));
3836 if(globals->locklist!=0) {
3837 id->id_InUse=DOSTRUE;
3839 else {
3840 id->id_InUse=DOSFALSE;
3846 BOOL checkchecksum(struct CacheBuffer *cb) {
3847 #ifdef CHECKCHECKSUMSALWAYS
3848 if(CALCCHECKSUM(globals->bytes_block,cb->data)==0) {
3849 #else
3850 if((cb->bits & CB_CHECKSUM)!=0 || CALCCHECKSUM(globals->bytes_block,cb->data)==0) { // -> copycachebuffer screws this up!
3851 #endif
3852 cb->bits|=CB_CHECKSUM;
3853 return(DOSTRUE);
3855 return(DOSFALSE);
3860 void setchecksum(struct CacheBuffer *cb) {
3861 struct fsBlockHeader *bh=cb->data;
3863 bh->be_checksum=0; /* Important! */
3864 bh->be_checksum=L2BE(-CALCCHECKSUM(globals->bytes_block,cb->data));
3869 LONG readcachebuffercheck(struct CacheBuffer **returnedcb,ULONG blckno,ULONG type) {
3870 LONG errorcode;
3871 struct fsBlockHeader *bh;
3873 while((errorcode=readcachebuffer(returnedcb,blckno))==0) {
3874 bh=(*returnedcb)->data;
3875 if(type!=0 && bh->id!=type) {
3876 dumpcachebuffers();
3877 outputcachebuffer(*returnedcb);
3878 emptycachebuffer(*returnedcb);
3880 if(blckno!=0) {
3881 if(request(PROGRAMNAME " request","%s\n"\
3882 "has a blockid error in block %ld.\n"\
3883 "Expected was blockid 0x%08lx,\n"\
3884 "but the block says it is blockid 0x%08lx.",
3885 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno,type,bh->id)<=0) {
3886 return(INTERR_BLOCK_WRONG_TYPE);
3889 else {
3890 /* if(request(PROGRAMNAME " request","%s\n"\
3891 "is not a DOS disk.",
3892 "Retry|Cancel",AROS_BSTR_ADDR(devnode->dn_Name),blckno,type,bh->id)<=0) {
3893 return(INTERR_BLOCK_WRONG_TYPE);
3894 } */
3896 return(INTERR_BLOCK_WRONG_TYPE);
3898 continue;
3900 if(checkchecksum(*returnedcb)==DOSFALSE) {
3901 dumpcachebuffers();
3902 outputcachebuffer(*returnedcb);
3903 emptycachebuffer(*returnedcb);
3905 if(request(PROGRAMNAME " request","%s\n"\
3906 "has a checksum error in block %ld.",
3907 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno)<=0) {
3908 return(INTERR_CHECKSUM_FAILURE);
3910 continue;
3912 if(BE2L(bh->be_ownblock)!=blckno) {
3913 dumpcachebuffers();
3914 outputcachebuffer(*returnedcb);
3915 emptycachebuffer(*returnedcb);
3917 if(request(PROGRAMNAME " request","%s\n"\
3918 "has a block error in block %ld.\n"\
3919 "Expected was block %ld,\n"\
3920 "but the block says it is block %ld.",
3921 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno,blckno,BE2L(bh->be_ownblock))<=0) {
3922 return(INTERR_OWNBLOCK_WRONG);
3924 continue;
3926 break;
3928 return(errorcode);
3933 /* How the CacheBuffers work:
3934 ==========================
3936 When the filesystem starts, the number of buffers indicated
3937 by the Mountlist are allocated. If the number of buffers
3938 is below the minimum then the filesystem will increase the
3939 number of buffers to the minimum. AddBuffers can be used
3940 later to change the number of CacheBuffers.
3942 Each CacheBuffer is linked into two chains. An LRU chain,
3943 to quickly determine the least recently used CacheBuffer,
3944 and a Hash chain. The Hash chain is used to quickly locate
3945 a CacheBuffer which contains a specific block. Empty
3946 cachebuffers are not linked in the Hash chain and have a
3947 blckno of zero.
3949 After having read a few blocks there comes a time when you
3950 want to modify them. The CacheBuffer systems helps to make
3951 it simple to do safe updates to blocks without ever running
3952 the risk of leaving the disk in a state where links or
3953 pointers are not yet valid. If you want to modify a block
3954 or series of blocks you need to tell this to the caching
3955 system by calling newtransaction(). An operation is a series
3956 of modifications which, when written to disk completely,
3957 would result in a valid disk. By calling newtransaction() you
3958 signal that you are about to start a series of modifications
3959 which together will again result in a valid disk.
3961 If however at any point during an operation somekind of
3962 error occurs which prevents you from finishing the operation
3963 you should call deletetransaction(). This will remove all
3964 changes you made from the point when you called
3965 newtransaction() from the cache. These blocks will now never
3966 get written to disk, so there is no chance that the disk
3967 becomes invalid by partial modifications.
3969 This sums up how the caching mechanism works. The internal
3970 workings of routines like newtransaction() and
3971 deletetransaction() will be explained later on. One very
3972 important routine hasn't been mentioned here:
3973 flushtransaction(). This is the routine which actually makes
3974 modifications to the disk. It does this by flushing one
3975 operation at a time to disk (in old to new order of course)
3976 in such a way that even if the machine crashed in the middle
3977 of the flush that the disk would remain valid. After
3978 flushing all operations the cache is cleaned up.
3985 /* Creating and moving objects
3987 1. Changing the comment on an object
3988 2. Changing the name of an object
3989 3. Creating a new object
3990 (4. Moving an object)
3992 Each of these three operations involves one or more of the following:
3994 1,2,3,4 - Locating enough new room for the object
3995 1,2,3,4 - Making sure the object-node points to the new block the
3996 object is located in.
3997 2 & 3 - Hashing the object
3998 3 - Allocating an ObjectNode.
4000 Order:
4002 'Allocating an ObjectNode' always precedes 'Hashing the Object' and 'Fixing the objectnode pointer'.
4003 'FindObjectSpace' always precedes 'Fixing the objectnode pointer'.
4005 1. FindObjectSpace OR Allocating an ObjectNode
4006 2. Fixing the objectnode pointer OR Hashing the Object */
4010 void fixlocks(struct GlobalHandle *gh,ULONG lastextentkey,ULONG lastextentoffset,ULONG filelength) {
4011 struct ExtFileLock *lock=globals->locklist;
4013 while(lock!=0) {
4014 if(lock->gh==gh) {
4015 if(filelength==0) {
4016 lock->offset=0;
4017 lock->extentoffset=0;
4018 lock->curextent=0;
4020 else if(lock->offset>filelength) {
4021 /* Hmmm, this lock is positioned beyond the EOF... */
4023 if(lock->offset-lock->extentoffset >= filelength) {
4024 lock->extentoffset=filelength-(lock->offset-lock->extentoffset);
4026 else {
4027 lock->extentoffset=filelength-lastextentoffset;
4028 lock->curextent=lastextentkey;
4030 lock->offset=filelength;
4033 lock=lock->next;
4039 static LONG extendblocksinfile(struct ExtFileLock *lock, ULONG blocks) {
4040 BLCK lastextentbnode;
4041 LONG errorcode;
4043 /* This function extends the file identified by the lock with a number
4044 of blocks. Only new Extents will be created -- the size of the file
4045 will not be altered, and changing it is left up to the caller. If
4046 the file did not have any blocks yet, then lock->curextent will be
4047 set to the first (new) ExtentBNode. It's left up up to the caller
4048 to reflect this change in object.file.data.
4050 This function must be called from within a transaction. */
4052 _XDEBUG((DEBUG_IO,"extendblocksinfile: Increasing number of blocks by %ld. lock->curextent = %ld\n",blocks,lock->curextent));
4054 if((errorcode=seektocurrent(lock))==0) {
4056 if((lastextentbnode=lock->curextent)!=0) {
4057 struct CacheBuffer *cb;
4058 struct fsExtentBNode *ebn;
4060 while((errorcode=findextentbnode(lastextentbnode,&cb,&ebn))==0 && BE2L(ebn->be_next)!=0) {
4061 lastextentbnode=BE2L(ebn->be_next);
4065 if(errorcode==0) {
4066 struct GlobalHandle *gh=lock->gh;
4068 /* We found the last Extent. Now we should see if we can extend it
4069 or if we need to add a new one and attach it to this one. */
4071 while(blocks!=0) {
4072 struct Space *sl=globals->spacelist;
4073 BLCK searchstart;
4075 if(lastextentbnode!=0) {
4076 struct CacheBuffer *cb;
4077 struct fsExtentBNode *ebn;
4079 if((errorcode=findextentbnode(lastextentbnode,&cb,&ebn))!=0) {
4080 break;
4082 searchstart=BE2L(ebn->be_key)+BE2W(ebn->be_blocks);
4084 else {
4085 searchstart=globals->block_rovingblockptr;
4088 if((errorcode=smartfindandmarkspace(searchstart,blocks))!=0) { /* only within transaction! */
4089 _DEBUG(("extendblocksinfile: sfams returned errorcode %ld\n", errorcode));
4090 break;
4093 _XDEBUG((DEBUG_IO,"extendblocksinfile: Found some space! blocks = %ld\n",blocks));
4095 while(blocks!=0 && sl->block!=0) {
4096 BLCK newspace=sl->block;
4097 ULONG newspaceblocks;
4099 newspaceblocks=sl->blocks > blocks ? blocks : sl->blocks;
4101 while(newspaceblocks!=0) {
4102 ULONG extentblocks;
4104 extentblocks=newspaceblocks > 8192 ? 8192 : newspaceblocks;
4106 newspaceblocks-=extentblocks;
4108 _XDEBUG((DEBUG_IO,"extendblocksinfile: sl->block = %ld, lastextentbnode = %ld, extentblocks = %ld\n",sl->block,lastextentbnode,extentblocks));
4110 if((errorcode=addblocks(extentblocks, newspace, gh->objectnode, &lastextentbnode))!=0) {
4111 _DEBUG(("extendblocksinfile: addblocks returned errorcode %ld\n", errorcode));
4112 return(errorcode);
4115 if(lock->curextent==0) {
4116 lock->curextent=lastextentbnode;
4119 lock->lastextendedblock=newspace + extentblocks;
4121 _XDEBUG((DEBUG_IO,"extendblocksinfile: Done adding or extending an extent -- blocks = %ld, extentblocks = %ld\n",blocks,extentblocks));
4123 blocks-=extentblocks;
4124 newspace+=extentblocks;
4126 sl++;
4132 return(errorcode);
4137 LONG truncateblocksinfile(struct ExtFileLock *lock,ULONG blocks,ULONG *lastextentkey,ULONG *lastextentoffset) {
4138 struct CacheBuffer *cb;
4139 struct fsExtentBNode *ebn;
4140 ULONG offset;
4141 ULONG extentoffset;
4142 LONG errorcode;
4144 /* Truncates the specified file to /blocks/ blocks. This function does not
4145 take care of setting the filesize. It also doesn't check for any locks
4146 which have a fileptr which is beyond the end of the file after calling
4147 this function. Its sole purpose is to reduce the number of blocks in
4148 the Extent list. */
4150 offset=blocks<<globals->shifts_block;
4152 _DEBUG(("truncateblocksinfile: truncating file by %ld blocks\n",blocks));
4154 if((errorcode=seekextent(lock,offset,&cb,&ebn,&extentoffset))==0) {
4156 _DEBUG(("truncateblocksinfile: offset = %ld, extentoffset = %ld\n",offset,extentoffset));
4158 if(offset-extentoffset==0) {
4159 ULONG prevkey=BE2L(ebn->be_prev);
4161 _DEBUG(("truncateblocksinfile: prevkey = %ld\n",prevkey));
4163 /* Darn. This Extent needs to be killed completely, meaning that we
4164 have to set the Next pointer of the previous Extent to zero. */
4166 deleteextents(BE2L(ebn->be_key));
4168 if((prevkey & 0x80000000)==0) {
4169 if((errorcode=findextentbnode(prevkey,&cb,&ebn))==0) {
4170 preparecachebuffer(cb);
4172 ebn->be_next=0;
4174 errorcode=storecachebuffer(cb);
4176 *lastextentkey=prevkey;
4177 *lastextentoffset=extentoffset-(BE2W(ebn->be_blocks)<<globals->shifts_block);
4180 else {
4181 struct fsObject *o;
4183 if((errorcode=readobject((prevkey & 0x7fffffff), &cb, &o))==0) {
4184 preparecachebuffer(cb);
4186 _DEBUG(("truncateblocksinfile: cb->blckno = %ld\n",cb->blckno));
4188 o->object.file.be_data=0;
4189 lock->gh->data=0;
4191 errorcode=storecachebuffer(cb);
4193 *lastextentkey=0;
4194 *lastextentoffset=0;
4198 else {
4199 ULONG newblocks=blocks-(extentoffset>>globals->shifts_block);
4201 *lastextentkey=BE2L(ebn->be_key);
4202 *lastextentoffset=extentoffset;
4204 _DEBUG(("truncateblocksinfile: newblocks = %ld, ebn->blocks = %ld\n",newblocks,BE2W(ebn->be_blocks)));
4206 if(newblocks!=BE2W(ebn->be_blocks)) {
4207 /* There is only one case where newblocks could equal en->blocks here,
4208 and that is if we tried to truncate the file to the same number of
4209 blocks the file already had! */
4211 lockcachebuffer(cb);
4213 if((errorcode=freespace(BE2L(ebn->be_key)+newblocks,BE2W(ebn->be_blocks)-newblocks))==0) {
4214 BLCK next=BE2L(ebn->be_next);
4216 preparecachebuffer(cb);
4218 ebn->be_blocks=W2BE(newblocks);
4219 ebn->be_next=0;
4221 if((errorcode=storecachebuffer(cb))==0) {
4222 errorcode=deleteextents(next); // Careful, deleting BNode's may change the location of other BNode's as well!
4226 unlockcachebuffer(cb);
4231 return(errorcode);
4236 LONG setfilesize(struct ExtFileLock *lock, ULONG bytes) {
4237 struct GlobalHandle *gh=lock->gh;
4238 LONG errorcode=0;
4240 /* This function sets the filesize of a file to /bytes/ bytes. If the
4241 file is not yet long enough, additional blocks are allocated and
4242 added to the file. If the file is too long the file will be shortened
4243 and the lost blocks are freed.
4245 Any filehandles using this file are set to the EOF if their current
4246 position would be beyond the new EOF when truncating the file.
4248 In any other case the position of the file ptr is not altered. */
4250 _DEBUG(("setfilesize: gh = 0x%08lx. Setting to %ld bytes. Current size is %ld bytes.\n",gh,bytes,gh->size));
4252 if(bytes!=gh->size) {
4253 LONG blocksdiff;
4254 LONG curblocks;
4256 curblocks=(gh->size+globals->bytes_block-1)>>globals->shifts_block;
4257 blocksdiff=((bytes+globals->bytes_block-1)>>globals->shifts_block) - curblocks;
4259 _DEBUG(("setfilesize: blocksdiff = %ld\n",blocksdiff));
4261 if(blocksdiff>0) {
4263 newtransaction();
4265 if((errorcode=extendblocksinfile(lock, blocksdiff))==0) {
4266 struct CacheBuffer *cb;
4267 struct fsObject *o;
4269 /* File (now) has right amount of blocks, only exact size in bytes may differ */
4271 if((errorcode=readobject(gh->objectnode, &cb, &o))==0) {
4272 preparecachebuffer(cb);
4274 o->object.file.be_size=L2BE(bytes);
4275 if(o->object.file.be_data==0) {
4276 o->object.file.be_data=L2BE(lock->curextent);
4279 errorcode=storecachebuffer(cb);
4283 if(errorcode==0) {
4284 endtransaction();
4285 gh->size=bytes;
4286 if(gh->data==0) {
4287 gh->data=lock->curextent;
4290 else {
4291 deletetransaction();
4294 else {
4295 ULONG lastextentkey=0;
4296 ULONG lastextentoffset=0;
4298 newtransaction();
4300 if(blocksdiff<0) {
4301 /* File needs to be shortened by -/blocksdiff/ blocks. */
4303 _DEBUG(("setfilesize: Decreasing number of blocks\n"));
4305 errorcode=truncateblocksinfile(lock,curblocks+blocksdiff,&lastextentkey,&lastextentoffset);
4308 _DEBUG(("setfilesize: lastextentkey = %ld, lastextentoffset = %ld\n",lastextentkey,lastextentoffset));
4310 if(errorcode==0) {
4311 struct CacheBuffer *cb;
4312 struct fsObject *o;
4314 /* File (now) has right amount of blocks, only exact size in bytes may differ */
4316 if((errorcode=readobject(gh->objectnode,&cb,&o))==0) {
4317 preparecachebuffer(cb);
4319 _DEBUG(("setfilesize: gh->objectnode = %ld, cb->blcno = %ld\n",gh->objectnode,cb->blckno));
4321 o->object.file.be_size=L2BE(bytes);
4323 errorcode=storecachebuffer(cb);
4327 if(errorcode==0) {
4328 endtransaction();
4329 gh->size=bytes;
4331 fixlocks(gh,lastextentkey,lastextentoffset,bytes);
4333 else {
4334 deletetransaction();
4338 if(errorcode==0) {
4339 lock->bits|=EFL_MODIFIED;
4343 return(errorcode);
4348 void seekforward(struct ExtFileLock *lock, UWORD ebn_blocks, BLCK ebn_next, ULONG bytestoseek) {
4350 /* This function does a simple forward seek. It assumes /bytestoseek/ is
4351 only large enough to skip within the current extent or to the very
4352 beginning of the next extent. */
4354 lock->offset+=bytestoseek;
4355 lock->extentoffset+=bytestoseek;
4356 if(lock->extentoffset >= ebn_blocks<<globals->shifts_block && ebn_next!=0) {
4357 lock->extentoffset=0;
4358 lock->curextent=ebn_next;
4364 LONG seektocurrent(struct ExtFileLock *lock) {
4365 LONG errorcode=0;
4367 /* This function checks if the currentextent is still valid. If not,
4368 it will attempt to locate the currentextent. */
4370 if(lock->curextent==0) {
4372 /* lock->curextent==0 can indicate 2 things:
4374 - A previously empty file was extended by another handle; in this
4375 case the file-ptr (lock->offset) of this handle is still zero.
4377 - The extent has been moved by the defragmenter, and will have to
4378 be re-located. In this case the file-ptr doesn't have to be
4379 zero. */
4381 if(lock->offset==0) {
4382 lock->curextent=lock->gh->data;
4384 else {
4385 struct CacheBuffer *cb;
4386 struct fsExtentBNode *ebn;
4387 ULONG extentoffset;
4389 if((errorcode=seekextent(lock, lock->offset, &cb, &ebn, &extentoffset))==0) {
4390 lock->curextent=BE2L(ebn->be_key);
4391 lock->extentoffset=lock->offset - extentoffset;
4396 return(errorcode);
4400 static LONG seek(struct ExtFileLock *lock,ULONG offset) {
4401 struct CacheBuffer *cb;
4402 struct fsExtentBNode *ebn;
4403 ULONG extentoffset;
4404 LONG errorcode;
4406 /* This function moves the file-ptr to the specified absolute file
4407 position. It will return an error if you try to seek beyond the
4408 end of the file. */
4410 _XDEBUG((DEBUG_SEEK,"seek: Attempting to seek to %ld\n",offset));
4412 if((errorcode=seekextent(lock,offset,&cb,&ebn,&extentoffset))==0) {
4413 lock->curextent=BE2L(ebn->be_key);
4414 lock->extentoffset=offset-extentoffset;
4415 lock->offset=offset;
4417 _XDEBUG((DEBUG_SEEK,"seek: lock->curextent = %ld, lock->extentoffset = %ld, lock->offset = %ld\n",lock->curextent,lock->extentoffset,lock->offset));
4420 _XDEBUG((DEBUG_SEEK,"seek: Exiting with errorcode %ld\n",errorcode));
4422 return(errorcode);
4427 LONG deleteextents(ULONG key) {
4428 struct CacheBuffer *cb;
4429 struct fsExtentBNode *ebn;
4430 LONG errorcode=0;
4432 /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it.
4433 This function DOES NOT fix the next pointer in a possible fsExtentBNode which
4434 might have been pointing to the first BNode we are deleting. Make sure you check
4435 this yourself, if needed.
4437 If key is zero, than this function does nothing.
4439 newtransaction() should have been called prior to calling this function. */
4441 _XDEBUG((DEBUG_NODES,"deleteextents: Entry -- deleting extents from key %ld\n",key));
4444 while(key!=0 && (errorcode=findbnode(globals->block_extentbnoderoot,key,&cb,(struct BNode **)&ebn))==0) {
4445 /* node to be deleted located. */
4447 // _XDEBUG((DDEBUG_NODES,"deleteextents: Now deleting key %ld. Next key is %ld\n",key,ebn->next));
4449 key=BE2L(ebn->be_next);
4451 lockcachebuffer(cb); /* Makes sure freespace() doesn't reuse this cachebuffer */
4453 if((errorcode=freespace(BE2L(ebn->be_key), BE2W(ebn->be_blocks)))==0) {
4454 unlockcachebuffer(cb);
4456 // _XDEBUG((DDEBUG_NODES,"deleteextents: deletebnode from root %ld, with key %ld\n",block_extentbnoderoot,ebn->key));
4458 if((errorcode=deletebnode(globals->block_extentbnoderoot,BE2L(ebn->be_key)))!=0) { /*** Maybe use deleteinternalnode here??? */
4459 break;
4462 else {
4463 unlockcachebuffer(cb);
4464 break;
4468 // _XDEBUG((DEBUG_NODES,"deleteextents: Exiting with errorcode %ld\n",errorcode));
4470 return(errorcode);
4475 static LONG findextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode) {
4476 LONG errorcode;
4478 errorcode=findbnode(globals->block_extentbnoderoot,key,returned_cb,(struct BNode **)returned_bnode);
4480 #ifdef CHECKCODE_BNODES
4481 if(*returned_bnode==0 || BE2L((*returned_bnode)->be_key)!=key) {
4482 dreq("findextentbnode: findbnode() can't find key %ld!",key);
4483 outputcachebuffer(*returned_cb);
4484 return(INTERR_BTREE);
4486 #endif
4488 return(errorcode);
4493 LONG findobjectnode(NODE nodeno,struct CacheBuffer **returned_cb,struct fsObjectNode **returned_node) {
4494 return(findnode(block_objectnoderoot, sizeof(struct fsObjectNode), nodeno, returned_cb, (struct fsNode **)returned_node));
4499 static inline LONG createextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode) {
4500 return(createbnode(globals->block_extentbnoderoot,key,returned_cb,(struct BNode **)returned_bnode));
4505 LONG seekextent(struct ExtFileLock *lock, ULONG offset, struct CacheBuffer **returned_cb, struct fsExtentBNode **returned_ebn, ULONG *returned_extentoffset) {
4506 BLCK extentbnode;
4507 LONG errorcode=0;
4509 /* When seeking there are 2 options; we start from the current extent,
4510 or we start from the first extent. Below we determine the best
4511 startpoint: */
4513 if(offset>lock->gh->size) {
4514 _XDEBUG((DEBUG_SEEK,"seekextent: Attempting to seek beyond file\n"));
4516 return(ERROR_SEEK_ERROR);
4519 // _DEBUG(("seekextent: offset = %ld, lock->offset = %ld, lock->extentoffset = %ld, lock->curextent = %ld\n",offset, lock->offset, lock->extentoffset, lock->curextent));
4521 if(lock->curextent!=0 && offset >= lock->offset - lock->extentoffset) {
4522 extentbnode=lock->curextent;
4523 *returned_extentoffset=lock->offset - lock->extentoffset;
4525 else {
4526 extentbnode=lock->gh->data;
4527 *returned_extentoffset=0;
4530 /* Starting point has been determined. Let the seeking begin!
4531 We keep getting the next extent, until we find the extent which
4532 contains the required offset. */
4534 if(extentbnode!=0) {
4535 while((errorcode=findextentbnode(extentbnode,returned_cb,returned_ebn))==0) {
4536 ULONG endbyte=*returned_extentoffset+(BE2W((*returned_ebn)->be_blocks)<<globals->shifts_block);
4538 if(offset>=*returned_extentoffset && offset<endbyte) {
4539 /* Hooray! We found the correct extent. */
4540 break;
4543 if(BE2L((*returned_ebn)->be_next)==0) {
4544 /* This break is here in case we run into the end of the file.
4545 This prevents *returned_extentoffset and extentbnode
4546 from being destroyed in the lines below, since it is still
4547 valid to seek to the EOF */
4549 if(offset>endbyte) {
4550 /* There where no more blocks, but there should have been... */
4551 errorcode=ERROR_SEEK_ERROR;
4553 break;
4556 *returned_extentoffset+=BE2W((*returned_ebn)->be_blocks)<<globals->shifts_block;
4557 extentbnode=BE2L((*returned_ebn)->be_next);
4561 return(errorcode);
4566 /* Notify support functions. */
4568 UBYTE *fullpath(struct CacheBuffer *cbstart,struct fsObject *o) {
4569 struct fsObjectContainer *oc=cbstart->data;
4570 struct CacheBuffer *cb=cbstart;
4571 UBYTE *path=&globals->pathstring[519];
4572 UBYTE *name;
4574 /* Returns the full path of an object, or 0 if it fails. */
4576 lockcachebuffer(cbstart);
4578 *path=0;
4580 while(oc->be_parent!=0) { /* Checking parent here means name in ROOT will be ignored. */
4581 name=o->name;
4582 while(*++name!=0) {
4585 while(name!=o->name) {
4586 *--path=upperchar(*--name);
4589 if(readobject(BE2L(oc->be_parent),&cb,&o)!=0) {
4590 path=0;
4591 break;
4594 oc=cb->data;
4596 if(oc->be_parent!=0) {
4597 *--path='/';
4601 unlockcachebuffer(cbstart);
4603 return(path);
4608 void checknotifyforpath(UBYTE *path,UBYTE notifyparent) {
4609 struct NotifyRequest *nr;
4610 UBYTE *s1,*s2;
4611 UBYTE *lastslash;
4613 /* /path/ doesn't have a trailing slash and start with a root directory (no colon) */
4615 s1=path;
4616 lastslash=path-1;
4617 while(*s1!=0) {
4618 if(*s1=='/') {
4619 lastslash=s1;
4621 s1++;
4624 nr=globals->notifyrequests;
4626 // _DEBUG(("checknotify: path = '%s'\n", path));
4628 while(nr!=0) {
4630 s1=path;
4631 s2=stripcolon(nr->nr_FullName);
4633 while(*s1!=0 && *s2!=0 && *s1==upperchar(*s2)) {
4634 s1++; // If last character doesn't match, this increment won't take place.
4635 s2++;
4638 /* "" == "hallo" -> no match
4639 "" == "/" -> match
4640 "" == "" -> match
4641 "/shit" == "" -> match (parent dir)
4642 "shit" == "" -> match (parent dir) */
4644 // _DEBUG(("checknotify: fullpath = '%s', nr->FullName = '%s'\n",s1,s2));
4646 if( (s1[0]==0 && (s2[0]==0 || (s2[0]=='/' && s2[1]==0))) || ((s2[0]==0 && (s1==lastslash || s1==lastslash+1)) && notifyparent==TRUE) ) {
4647 /* Wow, the string in the NotifyRequest matches! We need to notify someone! */
4649 _DEBUG(("checknotify: Notificating!! nr->FullName = %s, UserData = 0x%08lx\n",nr->nr_FullName, nr->nr_UserData));
4651 notify(nr);
4653 /* No else, if neither flag is set then do nothing. */
4656 nr = (struct NotifyRequest *)nr->nr_Next;
4662 void checknotifyforobject(struct CacheBuffer *cb,struct fsObject *o,UBYTE notifyparent) {
4663 checknotifyforpath(fullpath(cb,o),notifyparent);
4668 void notify(struct NotifyRequest *nr) {
4670 /* This function sends a Notify to the client indicated by the passed
4671 in notifyrequest structure. */
4673 if((nr->nr_Flags & NRF_SEND_SIGNAL)!=0) {
4674 /* Sending them a signal. */
4676 _DEBUG(("notify: Sending signal\n"));
4678 Signal(nr->nr_stuff.nr_Signal.nr_Task,1<<nr->nr_stuff.nr_Signal.nr_SignalNum);
4680 else if((nr->nr_Flags & NRF_SEND_MESSAGE)!=0) {
4681 struct NotifyMessage *nm;
4683 /* Sending them a message. */
4685 _DEBUG(("notify: Sending message\n"));
4687 if((nm=AllocMem(sizeof(struct NotifyMessage),MEMF_CLEAR))!=0) {
4688 nm->nm_ExecMessage.mn_ReplyPort=globals->msgportnotify;
4689 nm->nm_ExecMessage.mn_Length=sizeof(struct NotifyMessage);
4691 nm->nm_Class=NOTIFY_CLASS;
4692 nm->nm_Code=NOTIFY_CODE;
4693 nm->nm_NReq=(struct NotifyRequest *)nr;
4695 _DEBUG(("notify: PutMsg() - UserData = 0x%08lx\n", nr->nr_UserData));
4697 PutMsg(nr->nr_stuff.nr_Msg.nr_Port,(struct Message *)nm);
4707 #if 0
4708 LONG writedata(ULONG newbytes, ULONG extentblocks, BLCK newspace, UBYTE *data) {
4709 ULONG blocks=newbytes>>shifts_block;
4710 LONG errorcode=0;
4712 if(blocks>extentblocks) {
4713 blocks=extentblocks;
4716 if(blocks!=0 && (errorcode=write(newspace,data,blocks))!=0) {
4717 return(errorcode);
4720 if(blocks<extentblocks) {
4721 struct CacheBuffer *cb;
4723 _XDEBUG((DEBUG_IO," writedata: blocks = %ld, newbytes = %ld\n",blocks,newbytes));
4725 newbytes-=blocks<<shifts_block;
4726 data+=blocks<<shifts_block;
4728 if((cb=newcachebuffer(newspace+blocks))!=0) {
4729 unlockcachebuffer(cb);
4731 CopyMem(data,cb->data,newbytes);
4733 errorcode=writecachebuffer(cb);
4734 /* At this point we can do 2 things with this cachebuffer. We can leave it
4735 hashed so if the user requests part of this block again it can quickly be grabbed
4736 from this CacheBuffer. The other option is the call emptycachebuffer()
4737 and let this buffer be reused ASAP. */
4739 emptycachebuffer(cb);
4741 else {
4742 errorcode=ERROR_NO_FREE_STORE;
4746 return(errorcode);
4748 #endif
4757 static LONG addblocks(UWORD blocks, BLCK newspace, NODE objectnode, BLCK *io_lastextentbnode) {
4758 struct CacheBuffer *cb;
4759 struct fsExtentBNode *ebn;
4760 LONG errorcode;
4762 /* This function adds /blocks/ blocks starting at block /newspace/ to a file
4763 identified by /objectnode/ and /lastextentbnode/. /io_lastextentbnode/ can
4764 be zero if there is no ExtentBNode chain attached to this file yet.
4766 This function must be called from within a transaction.
4768 /blocks/ ranges from 1 to 8192. To be able to extend Extents which are
4769 almost full, it is wise to make this value no higher than 8192 blocks.
4771 /io_lastextentbnode/ will contain the new lastextentbnode value when this
4772 function completes.
4774 This function makes no attempt to update any locks associated with this file,
4775 nor does it update the filesize information in a possible globalhandle.
4777 If there was no chain yet, then this function will create a new one. However
4778 it will NOT update object.file.data -- this is left up to the caller. */
4780 if(*io_lastextentbnode!=0) {
4781 /* There was already a ExtentBNode chain for this file. Extending it. */
4783 _XDEBUG((DEBUG_IO," addblocks: Extending existing ExtentBNode chain.\n"));
4785 if((errorcode=findextentbnode(*io_lastextentbnode,&cb,&ebn))==0) {
4787 preparecachebuffer(cb);
4789 if(BE2L(ebn->be_key)+BE2W(ebn->be_blocks)==newspace && BE2W(ebn->be_blocks)+blocks<65536) {
4790 /* It is possible to extent the last ExtentBNode! */
4792 _XDEBUG((DEBUG_IO," addblocks: Extending last ExtentBNode.\n"));
4794 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+blocks);
4796 errorcode=storecachebuffer(cb);
4798 else {
4799 /* It isn't possible to extent the last ExtentBNode so we create
4800 a new one and link it to the last ExtentBNode. */
4802 ebn->be_next=L2BE(newspace);
4804 if((errorcode=storecachebuffer(cb))==0 && (errorcode=createextentbnode(newspace,&cb,&ebn))==0) {
4806 _XDEBUG((DEBUG_IO," addblocks: Created new ExtentBNode.\n"));
4808 ebn->be_key=L2BE(newspace);
4809 ebn->be_prev=L2BE(*io_lastextentbnode);
4810 ebn->be_next=0;
4811 ebn->be_blocks=W2BE(blocks);
4813 *io_lastextentbnode=newspace;
4815 if((errorcode=storecachebuffer(cb))==0) {
4816 globals->block_rovingblockptr=newspace+blocks;
4817 if((blocks<<globals->shifts_block) <= ROVING_SMALL_WRITE) {
4818 globals->block_rovingblockptr+=ROVING_RESERVED_SPACE>>globals->shifts_block;
4821 if(globals->block_rovingblockptr>=globals->blocks_total) {
4822 globals->block_rovingblockptr=0;
4829 else {
4830 /* There is no ExtentBNode chain yet for this file. Attaching one! */
4832 if((errorcode=createextentbnode(newspace,&cb,&ebn))==0) {
4834 _XDEBUG((DEBUG_IO," addblocks: Created new ExtentBNode chain.\n"));
4836 ebn->be_key=L2BE(newspace);
4837 ebn->be_prev=L2BE(objectnode+0x80000000);
4838 ebn->be_next=0;
4839 ebn->be_blocks=W2BE(blocks);
4841 *io_lastextentbnode=newspace;
4843 if((errorcode=storecachebuffer(cb))==0) {
4844 globals->block_rovingblockptr=newspace+blocks;
4845 if((blocks<<globals->shifts_block) <= ROVING_SMALL_WRITE) {
4846 globals->block_rovingblockptr+=ROVING_RESERVED_SPACE>>globals->shifts_block;
4849 if(globals->block_rovingblockptr>=globals->blocks_total) {
4850 globals->block_rovingblockptr=0;
4856 return(errorcode);
4861 static void diskchangenotify(ULONG class) {
4862 struct IOStdReq *inputreq;
4863 struct MsgPort *inputport;
4864 struct InputEvent ie;
4865 struct timeval tv;
4867 if((inputport=CreateMsgPort())!=0) {
4868 if((inputreq=(struct IOStdReq *)CreateIORequest(inputport,sizeof(struct IOStdReq)))!=0) {
4869 if(OpenDevice("input.device",0,(struct IORequest *)inputreq,0)==0) {
4871 GetSysTime(&tv);
4873 ie.ie_NextEvent=0;
4874 ie.ie_Class=class;
4875 ie.ie_SubClass=0;
4876 ie.ie_Code=0;
4877 ie.ie_Qualifier=IEQUALIFIER_MULTIBROADCAST;
4878 ie.ie_EventAddress=0;
4879 ie.ie_TimeStamp=tv;
4881 inputreq->io_Command = IND_WRITEEVENT;
4882 inputreq->io_Length = sizeof(struct InputEvent);
4883 inputreq->io_Data = &ie;
4885 DoIO((struct IORequest *)inputreq);
4887 CloseDevice((struct IORequest *)inputreq);
4889 DeleteIORequest(inputreq);
4891 DeleteMsgPort(inputport);
4897 #if 0
4899 () {
4900 struct ExtFileLock *lock;
4902 lock=locklist;
4904 while(lock!=0) {
4905 if((lock->bits & EFL_MODIFIED)!=0) {
4906 /* File belonging to this lock was modified. */
4908 if(lock->gh!=0 && lock->curextent!=0) {
4909 BLCK lastblock=lock->curextent;
4911 lastblock+=lock->extentoffset>>shifts_block;
4913 /* lastblock is now possibly the last block which is used by the file. This
4914 should always be true for newly created files which are being extended. */
4920 lock=lock->next;
4924 #endif
4927 BOOL freeupspace(void) {
4928 BOOL spacefreed=FALSE;
4930 /* This function tries to free up space. It does this by
4931 permanently deleting deleted files (if case of a recycled)
4932 and by flushing the current transaction if there is one.
4934 If no space could be freed, then this function returns
4935 FALSE. This is the go-ahead sign to report disk full
4936 to the user. */
4938 if(hastransaction()) {
4939 flushtransaction();
4940 spacefreed=TRUE;
4943 if(cleanupdeletedfiles()!=FALSE) {
4944 spacefreed=TRUE;
4947 return(spacefreed);
4952 LONG writetofile(struct ExtFileLock *lock, UBYTE *buffer, ULONG bytestowrite) {
4953 struct GlobalHandle *gh=lock->gh;
4954 LONG errorcode;
4956 /* This function must be called from within a transaction! */
4958 if((gh->protection & FIBF_WRITE)!=0) {
4959 ULONG maxbytes;
4960 LONG newbytes;
4962 /* First thing we need to do is extend the file (if needed) to
4963 accomodate for all the data we are about to write. */
4965 maxbytes=(gh->size + globals->bytes_block-1) & ~globals->mask_block; // Maximum number of bytes file can hold with the current amount of blocks.
4966 newbytes=bytestowrite-(maxbytes-lock->offset); // Number of new bytes which would end up in newly allocated blocks.
4968 if((errorcode=seektocurrent(lock))==0 && (newbytes<=0 || (errorcode=extendblocksinfile(lock, (newbytes+globals->bytes_block-1)>>globals->shifts_block))==0)) {
4969 struct CacheBuffer *extent_cb;
4970 struct fsExtentBNode *ebn=0;
4971 ULONG newfilesize;
4973 /* At this point, the file either didn't need extending, or was succesfully extended. */
4975 newfilesize=lock->offset+bytestowrite;
4977 if(newfilesize<gh->size) {
4978 newfilesize=gh->size;
4981 /* If the filesize will change, then we set it below */
4983 if(newfilesize!=gh->size) {
4984 struct CacheBuffer *cb;
4985 struct fsObject *o;
4987 if((errorcode=readobject(lock->objectnode,&cb,&o))==0) {
4988 preparecachebuffer(cb);
4990 checksum_writelong_be(cb->data, &o->object.file.be_size, newfilesize);
4991 gh->size=newfilesize;
4993 if(o->object.file.be_data==0) {
4994 checksum_writelong_be(cb->data, &o->object.file.be_data, lock->curextent);
4995 gh->data=lock->curextent;
4998 errorcode=storecachebuffer_nochecksum(cb);
5002 if(errorcode==0) {
5003 while(bytestowrite!=0 && (errorcode=findextentbnode(lock->curextent, &extent_cb, &ebn))==0) {
5004 ULONG bytes;
5005 ULONG offsetinblock=lock->extentoffset & globals->mask_block;
5006 BLCK ebn_next=BE2L(ebn->be_next);
5007 UWORD ebn_blocks=BE2W(ebn->be_blocks);
5009 if(BE2W(ebn->be_blocks)==lock->extentoffset>>globals->shifts_block) {
5010 /* We are at the end +1 of this extent. Skip to next one. */
5012 lock->curextent=BE2L(ebn->be_next);
5013 lock->extentoffset=0;
5014 continue;
5017 if(offsetinblock!=0 || bytestowrite<globals->bytes_block) {
5019 /** Partial writes to the last block of the file will cause a
5020 read of that block, even if this block didn't yet contain
5021 any valid data, because the file was just extended. This
5022 is unneeded... */
5024 /* File-ptr is located somewhere in the middle of a block, or at the
5025 start of the block but not at the end of the file. To add data
5026 to it we'll first need to read this block. */
5028 _XDEBUG((DEBUG_IO,"writetofile: Partially overwriting a single block of a file. ebn->key = %ld, lock->extentoffset = %ld\n",BE2L(ebn->be_key),lock->extentoffset));
5030 bytes=globals->bytes_block-offsetinblock;
5032 if(bytes>bytestowrite) {
5033 bytes=bytestowrite;
5036 // if(newbytes>0 && offsetinblock==0) { /** offsetinblock check is NOT redundant. */
5037 // struct CacheBuffer *cb=getcachebuffer();
5039 // CopyMem(buffer, cb->data, bytes);
5040 // errorcode=writethrough(lock->curextent + (lock->extentoffset>>shifts_block), cb->data, 1);
5041 // emptycachebuffer(cb);
5043 // if(errorcode!=0) {
5044 // break;
5045 // }
5046 // }
5047 // else {
5048 if((errorcode=writebytes(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, offsetinblock, bytes))!=0) {
5049 break;
5051 // }
5053 else {
5054 bytes=(((ULONG)ebn_blocks)<<globals->shifts_block) - lock->extentoffset;
5056 if(bytes > bytestowrite) {
5057 bytes=bytestowrite & ~globals->mask_block;
5059 /** This is a hack to speed up writes.
5061 What it does is write more bytes than there are in the buffer
5062 available (runaway writing), to avoid a seperate partial block
5063 write. It makes sure the extra data written doesn't extend
5064 into a new 4K page (assuming MMU is using 4K pages). */
5066 // #define MMU_PAGESIZE (524288)
5068 // if(((ULONG)(buffer+bytes) & ~(MMU_PAGESIZE-1))==((ULONG)(buffer+bytes+bytes_block-1) & ~(MMU_PAGESIZE-1))) {
5069 // bytes=bytestowrite;
5070 // }
5073 // _XDEBUG((DEBUG_IO,"writetofile: Writing multiple blocks: blockstowrite = %ld, ebn->key = %ld, lock->extentoffset = %ld, lock->offset = %ld\n",blockstowrite, ebn->key, lock->extentoffset, lock->offset));
5075 if((errorcode=write(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, (bytes+globals->bytes_block-1)>>globals->shifts_block))!=0) {
5076 break;
5080 seekforward(lock, ebn_blocks, ebn_next, bytes);
5082 bytestowrite-=bytes;
5083 buffer+=bytes;
5088 else {
5089 errorcode=ERROR_WRITE_PROTECTED;
5092 return(errorcode);
5097 LONG deletefileslowly(struct CacheBuffer *cbobject, struct fsObject *o) {
5098 ULONG size=BE2L(o->object.file.be_size);
5099 ULONG key=BE2L(o->object.file.be_data);
5100 LONG errorcode=0;
5102 /* cbobject & o refer to the file to be deleted (don't use this for objects
5103 other than files!). The file is deleted a piece at the time. This is
5104 because then even in low space situations files can be deleted.
5106 Note: This function deletes an object without first checking if
5107 this is allowed. Use deleteobject() instead. */
5109 _DEBUG(("deletefileslowly: Entry\n"));
5111 /* First we search for the last ExtentBNode */
5113 if(key!=0) {
5114 struct CacheBuffer *cb;
5115 struct fsExtentBNode *ebn;
5116 ULONG currentkey=0, prevkey=0;
5117 ULONG blocks=0;
5119 lockcachebuffer(cbobject);
5121 while((errorcode=findbnode(globals->block_extentbnoderoot,key,&cb,(struct BNode **)&ebn))==0) {
5122 if(BE2L(ebn->be_next)==0) {
5123 currentkey=BE2L(ebn->be_key);
5124 prevkey=BE2L(ebn->be_prev);
5125 blocks=BE2W(ebn->be_blocks);
5126 break;
5128 key=BE2L(ebn->be_next);
5131 /* Key could be zero (in theory) or contains the last ExtentBNode for this file. */
5133 if(errorcode==0) {
5134 while((key & 0x80000000)==0) {
5135 key=prevkey;
5137 newtransaction();
5139 lockcachebuffer(cb); /* Makes sure freespace() doesn't reuse this cachebuffer */
5141 if((errorcode=freespace(currentkey, blocks))==0) {
5142 unlockcachebuffer(cb);
5144 if((errorcode=deletebnode(globals->block_extentbnoderoot, currentkey))==0) {
5145 if((key & 0x80000000)==0) {
5146 if((errorcode=findbnode(globals->block_extentbnoderoot, key, &cb, (struct BNode **)&ebn))==0) {
5147 preparecachebuffer(cb);
5149 ebn->be_next=0;
5150 currentkey=BE2L(ebn->be_key);
5151 prevkey=BE2L(ebn->be_prev);
5152 blocks=BE2W(ebn->be_blocks);
5154 errorcode=storecachebuffer(cb);
5157 else {
5158 preparecachebuffer(cbobject);
5160 checksum_writelong_be(cbobject->data, &o->object.file.be_size, 0);
5161 checksum_writelong_be(cbobject->data, &o->object.file.be_data, 0);
5163 // o->object.file.data=0;
5164 // o->object.file.size=0;
5166 if((errorcode=storecachebuffer_nochecksum(cbobject))==0) {
5167 errorcode=setrecycledinfodiff(0, -((size+globals->bytes_block-1)>>globals->shifts_block));
5172 else {
5173 unlockcachebuffer(cb);
5176 if(errorcode==0) {
5177 /* Unlocked CacheBuffers could be killed after an endtransaction()! */
5178 endtransaction();
5180 else {
5181 deletetransaction();
5182 break;
5187 unlockcachebuffer(cbobject);
5190 _DEBUG(("deletefileslowly: Alternative way errorcode = %ld\n",errorcode));
5192 if(errorcode==0) {
5194 /* File-data was succesfully freed. Now remove the empty object. */
5196 newtransaction();
5198 if((errorcode=removeobject(cbobject, o))==0) {
5199 endtransaction();
5200 return(0);
5203 deletetransaction();
5206 _DEBUG(("deletefileslowly: Exiting with errorcode = %ld\n",errorcode));
5208 return(errorcode);
5214 Transactions
5215 ------------
5217 ReadCacheBuffer
5218 ReadOriginalCacheBuffer
5219 LockCacheBuffer
5220 UnLockCacheBuffer
5221 NewTransaction
5222 DeleteTransaction
5223 EndTransaction
5224 StoreCacheBuffer
5225 PrepareCacheBuffer
5227 The transaction system allows the filesystem to keep track
5228 of changes made to blocks in the cache. Every change is
5229 stored in a transaction buffer. The transaction buffer and
5230 the original version of a block can be used to restore the
5231 latest version of a block at any time.
5233 At any time you can use readcachebuffer() to get the latest
5234 version of a block. This could be a partially modified
5235 version if you're in the middle of a transaction, the latest
5236 version stored in the transaction buffer or the original
5237 version from disk.
5239 When you want to make changes to a block you call
5240 preparecachebuffer(). This checks if the block you're
5241 preparing to change is the original version, and if so makes
5242 a backup copy. This copy is not strictly needed, but it
5243 speeds up the filesystem to keep the original version around
5244 in case we need it later. The preparecachebuffer() function
5245 also calls lockcachebuffer() so the block you're changing
5246 won't be reused in subsequent cache operations.
5248 When you're satisfied with the changes you've made to a
5249 specific block you should call storecachebuffer(). This
5250 stores the changes into the transaction buffer. From that
5251 point on readcachebuffer() will be able to recreate the
5252 block with your new modifications.
5254 Before calling preparecachebuffer() for the first time, you
5255 need to call newtransaction(). This is to let the
5256 transaction system know you're about to start a new series
5257 of modifications.
5259 When you're done making all the changes to multiple blocks
5260 you need to call endtransaction(). This makes all the
5261 changes permanent. If there was an error along the way you
5262 can call deletetransaction() and all the changes you stored
5263 with storecachebuffer() since you're last call to
5264 newtransaction() will automatically be discarded.
5266 Any cachebuffers you had locked at the time which had
5267 changes in them will be restored to their original state (in
5268 reality these buffers will simply be reread using
5269 readcachebuffer()).
5271 Examples:
5273 x=readcachebuffer(0); // x.data = 1
5275 newtransaction();
5277 preparecachebuffer(x);
5279 x.data=2;
5281 storecachebuffer(cb);
5283 deletetransaction(); // x.data = 1;
5285 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); endtr() -> "CC";
5287 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); deltr() -> "AA";
5289 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); newtr(); prep(x); write("EE") -> "EE"; store(x); deltr() -> "CC"; deltr() -> "AA";
5297 LONG setnextextent(BLCK next, BLCK key) {
5298 struct CacheBuffer *cb;
5299 struct fsExtentBNode *ebn;
5300 LONG errorcode=0;
5302 /* This function set the previous value of the
5303 passed Extent. If the passed Extent is zero
5304 then this function does nothing. */
5306 if(next!=0 && (errorcode=findextentbnode(next, &cb, &ebn))==0) {
5307 preparecachebuffer(cb);
5309 ebn->be_prev=L2BE(key);
5311 errorcode=storecachebuffer(cb);
5314 return(errorcode);
5319 LONG setprevextent(BLCK prev, BLCK key) {
5320 struct CacheBuffer *cb;
5321 struct fsExtentBNode *ebn;
5322 LONG errorcode;
5324 /* This function sets the next value of the
5325 passed Extent/Object to key. */
5327 if((prev & 0x80000000)==0 && (errorcode=findextentbnode(prev, &cb, &ebn))==0) {
5328 preparecachebuffer(cb);
5330 ebn->be_next=L2BE(key);
5332 errorcode=storecachebuffer(cb);
5334 else {
5335 struct fsObject *o;
5337 if((errorcode=readobject((prev & 0x7fffffff), &cb, &o))==0) {
5338 preparecachebuffer(cb);
5340 o->object.file.be_data=L2BE(key);
5342 errorcode=storecachebuffer(cb);
5346 return(errorcode);
5351 LONG mergeextent(BLCK key) {
5352 struct CacheBuffer *cb;
5353 struct fsExtentBNode *ebn;
5354 LONG errorcode;
5356 /* This function tries to merge the current extent with the
5357 next extent. If the extent can't be merged, or was
5358 succesfully merged then this function returns 0. Any
5359 error which occured during merging will be returned. */
5361 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5363 /* We check if we found the right key, if there is a next Extent and
5364 if the two Extents touch each other: */
5366 if(BE2L(ebn->be_key)==key && BE2L(ebn->be_next)!=0 && BE2L(ebn->be_key)+BE2W(ebn->be_blocks) == BE2L(ebn->be_next)) {
5367 struct CacheBuffer *cb2;
5368 struct fsExtentBNode *ebn2;
5370 if((errorcode=findextentbnode(BE2L(ebn->be_next), &cb2, &ebn2))==0 && BE2W(ebn2->be_blocks)+BE2W(ebn->be_blocks) < 65536) {
5371 BLCK next=BE2L(ebn2->be_next);
5373 /* Merge next extent with our extent */
5375 preparecachebuffer(cb);
5377 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+BE2W(ebn2->be_blocks));
5378 ebn->be_next=L2BE(next);
5380 if((errorcode=storecachebuffer(cb))==0) { // call storecachebuffer() here, because deletebnode() may move our BNode.
5382 if((errorcode=deletebnode(globals->block_extentbnoderoot, BE2L(ebn2->be_key)))==0) { /*** Maybe use deleteinternalnode here??? */
5383 errorcode=setnextextent(next, key);
5390 return(errorcode);
5395 LONG insertextent(BLCK key, BLCK next, BLCK prev, ULONG blocks) {
5396 struct CacheBuffer *cb=0;
5397 struct fsExtentBNode *ebn=0;
5398 LONG errorcode;
5400 /* This function creates a new extent, but won't create one if it can
5401 achieve the same effect by extending the next or previous extent.
5402 In the unlikely case that the new extent is located EXACTLY between
5403 its predecessor and successor then an attempt will be made to merge
5404 these 3 extents into 1. */
5407 prev available & mergeable -> merge.
5408 prev available, but not mergeable -> create new -> update next -> update previous.
5409 prev not available -> create new -> update next -> update object
5412 if((prev & 0x80000000)!=0 || (errorcode=findextentbnode(prev, &cb, &ebn))==0) {
5414 if((prev & 0x80000000)==0 && prev+BE2W(ebn->be_blocks) == key && BE2W(ebn->be_blocks)+blocks < 65536) {
5416 /* Extent we are inserting is mergeable with its previous extent. */
5418 preparecachebuffer(cb);
5420 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+blocks); /* This merges the previous and the new extent */
5422 if((errorcode=storecachebuffer(cb))==0) {
5423 errorcode=mergeextent(BE2L(ebn->be_key));
5426 else {
5428 /* Extent we are inserting couldn't be merged with its previous extent. */
5430 if((errorcode=setprevextent(prev, key))==0 && (errorcode=createextentbnode(key, &cb, &ebn))==0) {
5432 /* Succesfully updated previous extent, or the object. Also created new BNode */
5434 ebn->be_key=L2BE(key);
5435 ebn->be_prev=L2BE(prev);
5436 ebn->be_next=L2BE(next);
5437 ebn->be_blocks=BE2W(blocks);
5439 if((errorcode=storecachebuffer(cb))==0) {
5440 if((errorcode=setnextextent(next, key))==0) {
5441 mergeextent(key);
5448 return(errorcode);
5453 LONG truncateextent(BLCK key, LONG blocks) {
5454 struct CacheBuffer *cb;
5455 struct fsExtentBNode *ebn;
5456 LONG errorcode=0;
5458 /* Truncates the extended by the given amount of blocks.
5459 If the amount is negative then the truncation occurs
5460 at the start, otherwise at the end.
5462 This function returns INTERR_EXTENT if you tried to
5463 truncate to zero blocks (or beyond). Any errorcode
5464 is returned. If blocks is zero, then this function
5465 does nothing.
5467 Mar 20 1999: The truncation at the start could cause
5468 BNode's to get lost (because they are
5469 indexed by their start block). Fixed. */
5471 if(blocks!=0) {
5472 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5473 ULONG b;
5475 b=blocks<0 ? -blocks : blocks;
5477 if(b<BE2W(ebn->be_blocks) && BE2L(ebn->be_key)==key) {
5478 if(blocks<0) {
5479 ULONG next=BE2L(ebn->be_next);
5480 ULONG prev=BE2L(ebn->be_prev);
5481 UWORD blocks=BE2W(ebn->be_blocks)-b;
5483 /* Truncating at the start. */
5485 if((errorcode=deletebnode(globals->block_extentbnoderoot, key))==0) {
5487 key+=b;
5489 if((errorcode=createbnode(globals->block_extentbnoderoot, key, &cb, (struct BNode **)&ebn))==0) {
5490 ebn->be_key=L2BE(key);
5491 ebn->be_next=L2BE(next);
5492 ebn->be_prev=L2BE(prev);
5493 ebn->be_blocks=W2BE(blocks);
5495 if((errorcode=storecachebuffer(cb))==0) {
5496 /* Truncating at start means changing the key value. This
5497 means that the next and previous BNode's must also be
5498 adjusted. */
5500 if((errorcode=setnextextent(next, key))==0) {
5501 errorcode=setprevextent(prev, key);
5507 else {
5509 /* Truncating at the end. */
5511 preparecachebuffer(cb);
5513 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)-b);
5515 errorcode=storecachebuffer(cb);
5518 else {
5519 errorcode=INTERR_EXTENT;
5524 return(errorcode);
5529 static LONG copy(BLCK source, BLCK dest, ULONG totblocks, UBYTE *optimizebuffer) {
5530 ULONG blocks;
5531 LONG errorcode=0;
5533 /* Low-level function to copy blocks from one place to another. */
5535 while(totblocks!=0) {
5536 blocks=totblocks;
5537 if(blocks > (OPTBUFSIZE>>globals->shifts_block)) {
5538 blocks=OPTBUFSIZE>>globals->shifts_block;
5541 if((errorcode=read(source, optimizebuffer, blocks))!=0) {
5542 break;
5545 if((errorcode=write(dest, optimizebuffer, blocks))!=0) {
5546 break;
5549 totblocks-=blocks;
5550 source+=blocks;
5551 dest+=blocks;
5554 return(errorcode);
5559 LONG deleteextent(struct CacheBuffer *cb, struct fsExtentBNode *ebn) {
5560 BLCK next,prev;
5561 LONG errorcode;
5563 /* Deletes an fsExtentBNode structure and properly relinks the rest of the chain.
5564 No space will be given free.
5566 newtransaction() should have been called prior to calling this function. */
5568 next=BE2L(ebn->be_next);
5569 prev=BE2L(ebn->be_prev);
5571 if((errorcode=deletebnode(globals->block_extentbnoderoot,BE2L(ebn->be_key)))==0) { /*** Maybe use deleteinternalnode here??? */
5572 if((errorcode=setnextextent(next, prev))==0) {
5573 errorcode=setprevextent(prev, next);
5577 return(errorcode);
5582 static WORD enough_for_add_moved(void) {
5583 if(globals->defragmentlongs>5) {
5584 return TRUE;
5586 return FALSE;
5589 static inline void add_moved(ULONG blocks, ULONG from, ULONG to) {
5590 if(globals->defragmentsteps!=0) {
5591 *globals->defragmentsteps++=AROS_LONG2BE(MAKE_ID('M','O','V','E'));
5592 *globals->defragmentsteps++=3;
5593 *globals->defragmentsteps++=blocks;
5594 *globals->defragmentsteps++=from;
5595 *globals->defragmentsteps++=to;
5596 *globals->defragmentsteps=0;
5598 globals->defragmentlongs-=5;
5602 static inline void add_done(void) {
5603 if(globals->defragmentsteps!=0) {
5604 *globals->defragmentsteps++=AROS_LONG2BE(MAKE_ID('D','O','N','E'));
5605 *globals->defragmentsteps++=0;
5606 *globals->defragmentsteps=0;
5608 globals->defragmentlongs-=2;
5614 LONG moveextent(struct fsExtentBNode *ebn, BLCK dest, UWORD blocks) {
5615 UBYTE *buf;
5616 LONG errorcode;
5618 /* This function (partially) moves the Extent to /dest/.
5619 /blocks/ is the number of blocks moved; if blocks is
5620 equal to the size of the Extent, then the Extent will
5621 be deleted. Else the start of the Extent will be
5622 truncated. */
5624 if((buf=AllocVec(OPTBUFSIZE, globals->bufmemtype))!=0) {
5625 BLCK key=BE2L(ebn->be_key);
5626 BLCK next=BE2L(ebn->be_next);
5627 BLCK prev=BE2L(ebn->be_prev);
5628 UWORD blocksinextent=BE2W(ebn->be_blocks);
5630 if((errorcode=copy(key, dest, blocks, buf))==0) { // This functions knows that OPTBUFSIZE is the size of the buffer!
5632 /* Data has been physically moved -- nothing has been
5633 permanently altered yet. */
5635 if((errorcode=markspace(dest, blocks))==0) {
5637 if((errorcode=freespace(key, blocks))==0) {
5639 /* Bitmap has been altered to reflect the new location of the
5640 moved data. Now we either need to truncate the Extent (if
5641 it was partially moved) or remove it. */
5643 if(blocksinextent==blocks) {
5644 struct CacheBuffer *cb;
5646 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5647 errorcode=deleteextent(cb, ebn);
5650 else {
5651 next=key+blocks;
5652 errorcode=truncateextent(key, -blocks);
5655 if(errorcode==0) {
5656 if((errorcode=insertextent(dest, next, prev, blocks))==0) {
5657 add_moved(blocks, key, dest);
5664 FreeVec(buf);
5666 else {
5667 errorcode=ERROR_NO_FREE_STORE;
5670 return(errorcode);
5675 static LONG fillgap(BLCK key) {
5676 struct CacheBuffer *cb;
5677 struct fsExtentBNode *ebn;
5678 LONG errorcode;
5680 /* This function will attempt to fill the gap behind an extent with
5681 data from the next extent. This in effect merges the extent and
5682 the next extent (partially). */
5684 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5685 ULONG next=BE2L(ebn->be_next);
5687 key+=BE2W(ebn->be_blocks);
5689 while(next!=0 && (errorcode=findextentbnode(next, &cb, &ebn))==0) { // !! failed !!
5690 UWORD blocks=BE2W(ebn->be_blocks);
5691 LONG free;
5693 lockcachebuffer(cb);
5695 if((free=availablespace(key, 1024))>0) {
5697 unlockcachebuffer(cb);
5699 _DEBUG(("fillgap: availablespace() returned %ld\n",free));
5701 /* The gap consists of /free/ blocks. */
5703 if(free > blocks && enough_for_add_moved()!=FALSE) {
5704 next=BE2L(ebn->be_next);
5706 else {
5707 next=0;
5710 if((errorcode=moveextent(ebn, key, MIN(free, blocks)))!=0) {
5711 return(errorcode);
5714 key+=blocks;
5716 else {
5717 unlockcachebuffer(cb);
5722 _DEBUG(("fillgap: exiting with errorcode %ld\n",errorcode));
5724 return(errorcode);
5729 LONG getbnode(BLCK block, struct CacheBuffer **returned_cb, struct fsExtentBNode **returned_ebn) {
5730 LONG errorcode;
5732 /* This function gets the ExtentBNode which starts at the given
5733 block or the first one after the given block. Zero *ebn indicates
5734 there were no ExtentBNode's at or after the given block. */
5736 if((errorcode=findbnode(globals->block_extentbnoderoot, block, returned_cb, (struct BNode **)returned_ebn))==0) {
5738 _DEBUG(("getbnode: ebn->key = %ld, ebn->prev = %ld, ebn->blocks = %ld\n",BE2L((*returned_ebn)->be_key), BE2L((*returned_ebn)->be_prev), BE2W((*returned_ebn)->be_blocks)));
5740 if(*returned_ebn!=0 && BE2L((*returned_ebn)->be_key)<block) {
5741 errorcode=nextbnode(globals->block_extentbnoderoot, returned_cb, (struct BNode **)returned_ebn);
5743 _DEBUG(("getbnode: 2: ebn->key = %ld, ebn->prev = %ld, ebn->blocks = %ld\n",BE2L((*returned_ebn)->be_key), BE2L((*returned_ebn)->be_prev), BE2W((*returned_ebn)->be_blocks)));
5747 return(errorcode);
5752 #if 0
5753 LONG makefreespace(BLCK block) {
5754 struct CacheBuffer *cb;
5755 struct fsExtentBNode *ebn;
5756 LONG errorcode;
5758 /* This function tries to move the data located at /block/ to
5759 another area of the disk. */
5761 if((errorcode=findextentbnode(block, &cb, &ebn))==0) {
5762 ULONG blocks;
5763 ULONG newblocks;
5764 BLCK startblock;
5766 lockcachebuffer(cb);
5768 blocks=MIN(OPTBUFSIZE>>shifts_block, ebn->blocks);
5770 if((errorcode=findspace2_backwards(blocks, block, blocks_total, &startblock, &newblocks))==0) {
5771 unlockcachebuffer(cb);
5773 if(newblocks!=0) {
5775 _DEBUG(("makefreespace: Looking for %ld blocks from block %ld, and found %ld blocks at block %ld.\n", blocks, block, newblocks, startblock));
5777 errorcode=moveextent(ebn, startblock, newblocks);
5779 else {
5780 errorcode=ERROR_DISK_FULL;
5783 else {
5784 unlockcachebuffer(cb);
5788 return(errorcode);
5790 #endif
5793 LONG makefreespace(BLCK block) {
5794 struct CacheBuffer *cb;
5795 struct fsExtentBNode *ebn;
5796 LONG errorcode;
5797 WORD count=0;
5799 /* This function tries to move the data located at /block/ to
5800 another area of the disk. */
5802 if((errorcode=findextentbnode(block, &cb, &ebn))==0) {
5803 do {
5804 ULONG blocks;
5805 ULONG newblocks;
5806 BLCK startblock;
5808 lockcachebuffer(cb);
5810 blocks=MIN(OPTBUFSIZE>>globals->shifts_block, BE2W(ebn->be_blocks));
5812 if((errorcode=findspace2_backwards(blocks, BE2L(ebn->be_key), globals->blocks_total, &startblock, &newblocks))==0) { // ebn->key should not be changed to block.
5813 unlockcachebuffer(cb);
5815 if(newblocks!=0) {
5817 _DEBUG(("makefreespace: Looking for %ld blocks from block %ld, and found %ld blocks at block %ld.\n", blocks, block, newblocks, startblock));
5819 if((errorcode=moveextent(ebn, startblock, newblocks))!=0) {
5820 break;
5823 else {
5824 if(count==0) {
5825 errorcode=ERROR_DISK_FULL;
5827 break;
5830 else {
5831 unlockcachebuffer(cb);
5832 break;
5835 if(count++>=1) {
5836 break;
5839 if((errorcode=getbnode(block, &cb, &ebn))!=0 || ebn==0) {
5840 break;
5843 } while((BE2L(ebn->be_prev) & 0x80000000)==0);
5846 return(errorcode);
5851 LONG skipunmoveable(BLCK block) {
5852 struct CacheBuffer *cb;
5853 struct fsExtentBNode *ebn;
5855 /* This function looks for moveable data or free space starting
5856 from the given block. It returns -1 in case of failure, or
5857 the first moveable block it finds. If there ain't no more
5858 moveable blocks, then blocks_total is returned. */
5860 if((findbnode(globals->block_extentbnoderoot, block, &cb, (struct BNode **)&ebn))==0) {
5861 if(ebn!=0 && ebn->be_key==L2BE(block)) {
5862 return((LONG)block);
5864 else if(ebn==0 || BE2L(ebn->be_key)>=block || nextbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn)==0) {
5865 if(ebn!=0) {
5866 BLCK key=BE2L(ebn->be_key);
5867 LONG used;
5869 /* Found something moveable, but maybe there was some free space before the
5870 moveable Extent. */
5872 if((used=allocatedspace(block, key-block))!=-1) {
5873 if(block+used<key) {
5874 return((LONG)block+used);
5876 else {
5877 return((LONG)key);
5881 else {
5882 LONG errorcode;
5884 if((errorcode=findspace(1, block, globals->blocks_total, &block))==0) {
5885 return((LONG)block);
5887 else if(errorcode==ERROR_DISK_FULL) {
5888 return((LONG)globals->blocks_total);
5894 return(-1);
5898 struct fsExtentBNode *startofextentbnodechain(struct fsExtentBNode *ebn) {
5899 struct CacheBuffer *cb;
5900 LONG errorcode=0;
5902 while((BE2L(ebn->be_prev) & 0x80000000)==0 && (errorcode=findextentbnode(BE2L(ebn->be_prev), &cb, &ebn))==0) {
5905 if(errorcode==0) {
5906 return(ebn);
5909 return(0);
5914 #if 0
5915 LONG findmatch(BLCK startblock, ULONG blocks, ULONG *bestkey) {
5916 struct CacheBuffer *cb;
5917 struct fsExtentBNode *ebn;
5918 // struct fsExtentBNode *ebn_start;
5919 ULONG bestblocks=0;
5920 LONG errorcode;
5922 /* This function looks for the start of a ExtentBNode chain
5923 which matches the given size. If none is found, then the
5924 next smaller chain is returned. If none is found, then a
5925 larger chain is returned. If there are no chains at all
5926 0 is returned.
5928 The first ExtentBNode examined is determined by the start
5929 block number which is passed. */
5931 _DEBUG(("findmatch: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
5933 *bestkey=0;
5935 if((errorcode=getbnode(startblock, &cb, &ebn))==0 && ebn!=0) {
5937 _DEBUG(("findmatch: 1, ebn->key = %ld, ebn->blocks = %ld\n", ebn->key, ebn->blocks));
5939 do {
5940 struct CacheBuffer *cb2;
5941 struct fsExtentBNode *ebn_start=ebn;
5942 struct fsObject *o;
5943 ULONG total=ebn->blocks; /* If larger than bestblocks, then we can stop looking for the start of the ExtentBNode chain early. */
5944 ULONG key, newblocks;
5946 lockcachebuffer(cb);
5947 // ebn_start=startofextentbnodechain(ebn);
5949 struct CacheBuffer *cb;
5951 while((total<bestblocks || bestblocks==0) && (ebn_start->prev & 0x80000000)==0 && ebn_start->prev >= ebn->key && (errorcode=findextentbnode(ebn_start->prev, &cb, &ebn_start))==0) {
5952 total+=ebn_start->blocks;
5955 unlockcachebuffer(cb);
5957 if(errorcode!=0) {
5958 break;
5961 if((total<bestblocks || bestblocks==0) && (ebn_start->prev & 0x80000000)!=0) {
5963 _DEBUG(("findmatch: 2, ebn->key = %ld\n", ebn->key));
5965 key=ebn_start->key;
5967 lockcachebuffer(cb);
5968 errorcode=readobject(ebn_start->prev & 0x7FFFFFFF, &cb2, &o);
5969 unlockcachebuffer(cb);
5971 if(errorcode!=0) {
5972 break;
5975 _DEBUG(("findmatch: 3, filesize = %ld\n",o->object.file.size));
5977 newblocks=(o->object.file.size+bytes_block-1)>>shifts_block;
5979 if(newblocks==blocks) { /* Found ideal match */
5980 *bestkey=key;
5981 break;
5983 else if(newblocks<blocks) {
5984 if(newblocks>bestblocks || bestblocks>blocks) {
5985 *bestkey=key;
5986 bestblocks=newblocks;
5989 else if(bestblocks==0 || newblocks<bestblocks) {
5990 *bestkey=key;
5991 bestblocks=newblocks;
5995 } while((errorcode=nextbnode(block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0);
5998 return(errorcode);
6000 #endif
6005 void newfragmentinit_large(ULONG blocks) {
6006 globals->bestkey=0;
6007 globals->bestblocks=0;
6008 globals->searchedblocks=blocks;
6013 ULONG newfragment_large(ULONG block, ULONG blocks) {
6014 if(blocks==globals->searchedblocks) { /* Found ideal match */
6015 return(block);
6017 else if(blocks<globals->searchedblocks) {
6018 if(blocks>globals->bestblocks || globals->bestblocks>globals->searchedblocks) {
6019 globals->bestkey=block;
6020 globals->bestblocks=blocks;
6023 else if(globals->bestblocks==0 || blocks<globals->bestblocks) {
6024 globals->bestkey=block;
6025 globals->bestblocks=blocks;
6028 return(0);
6033 #if 0
6034 ULONG newfragment_large(ULONG block, ULONG blocks, UBYTE type) {
6035 if(type==0) {
6036 if(blocks==searchedblocks) { /* Found ideal match */
6037 return(block);
6039 else if(blocks<searchedblocks) {
6040 if(blocks>bestblocks || bestblocks>searchedblocks) {
6041 bestkey=block;
6042 bestblocks=blocks;
6045 else if(bestblocks==0 || blocks<bestblocks) {
6046 bestkey=block;
6047 bestblocks=blocks;
6051 return(0);
6053 #endif
6056 ULONG newfragmentend_large(void) {
6057 return(globals->bestkey);
6063 void newfragmentinit_small(ULONG blocks) {
6064 ULONG *f=globals->fragment;
6065 WORD n=FRAGMENTS;
6067 newfragmentinit_large(blocks);
6069 while(--n>=0) {
6070 *f++=0;
6079 void newfragmentinit_small(ULONG blocks) {
6080 ULONG *f=fragment;
6081 UBYTE *fb=fragmenttype;
6082 WORD n=FRAGMENTS;
6084 newfragmentinit_large(blocks);
6086 while(--n>=0) {
6087 *f++=0;
6088 *fb++=0;
6094 ULONG newfragment_small(ULONG block, ULONG blocks, UBYTE type) {
6095 if(blocks<searchedblocks) {
6096 if(fragment[blocks]==0 || (fragmenttype[blocks]!=0 && type==0)) {
6097 ULONG blocks2=searchedblocks-blocks;
6099 fragment[blocks]=block;
6100 fragmenttype[blocks]=type;
6102 if(blocks2==blocks) {
6103 blocks2=0;
6106 if(fragment[blocks2]!=0) {
6107 if(fragmenttype[blocks]==0 && fragmenttype[blocks2]==0) {
6108 return(fragment[blocks]);
6114 return(newfragment_large(block, blocks, type));
6120 ULONG newfragment_small(ULONG block, ULONG blocks) {
6121 if(blocks<globals->searchedblocks && globals->fragment[blocks]==0) {
6122 ULONG blocks2=globals->searchedblocks-blocks;
6124 globals->fragment[blocks]=block;
6126 if(blocks2==blocks) {
6127 blocks2=0;
6130 if(globals->fragment[blocks2]!=0) {
6131 return(globals->fragment[blocks]);
6135 return(newfragment_large(block, blocks));
6140 ULONG newfragmentend_small(void) {
6141 return(newfragmentend_large());
6147 #if 0
6148 LONG findmatch(BLCK startblock, ULONG blocks, ULONG *bestkey) {
6149 struct CacheBuffer *cb;
6150 struct fsExtentBNode *ebn;
6151 ULONG lastextentend=0;
6152 LONG maxscan=defrag_maxfilestoscan;
6153 LONG errorcode;
6155 /* This function looks for the start of a ExtentBNode chain
6156 which matches the given size. If none is found, then the
6157 next smaller chain is returned. If none is found, then a
6158 larger chain is returned. If there are no chains at all
6159 0 is returned.
6161 The first ExtentBNode examined is determined by the start
6162 block number which is passed. */
6164 _DEBUG(("findmatch: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
6166 *bestkey=0;
6168 if(blocks>FRAGMENTS-1) {
6169 newfragmentinit_large(blocks);
6171 else {
6172 newfragmentinit_small(blocks);
6175 if((errorcode=getbnode(startblock, &cb, &ebn))==0 && ebn!=0) {
6177 _DEBUG(("findmatch: ebn->key = %ld, ebn->blocks = %ld\n", ebn->key, ebn->blocks));
6179 do {
6180 if((ebn->prev & 0x80000000)!=0) {
6181 struct CacheBuffer *cb2;
6182 struct fsObject *o;
6183 ULONG newblocks;
6184 UBYTE fragmenttype;
6186 /* Found the start of a candidate chain. */
6188 lockcachebuffer(cb);
6189 errorcode=readobject(ebn->prev & 0x7FFFFFFF, &cb2, &o);
6190 unlockcachebuffer(cb);
6192 if(errorcode!=0) {
6193 break;
6196 newblocks=(o->object.file.size+bytes_block-1)>>shifts_block;
6198 fragmenttype=lastextentend==ebn->key ? 1 : 0;
6200 if(blocks>FRAGMENTS-1) {
6201 *bestkey=newfragment_large(ebn->key, newblocks, fragmenttype);
6203 else {
6204 *bestkey=newfragment_small(ebn->key, newblocks, fragmenttype);
6207 if(*bestkey!=0) {
6208 return(0);
6211 if(--maxscan<0) {
6212 break;
6215 lastextentend=ebn->next==0 ? ebn->key + ebn->blocks : 0;
6217 } while((errorcode=nextbnode(block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0);
6219 if(errorcode==0) {
6220 if(blocks>FRAGMENTS-1) {
6221 *bestkey=newfragmentend_large();
6223 else {
6224 *bestkey=newfragmentend_small();
6229 return(errorcode);
6231 #endif
6235 LONG findmatch_fromend(BLCK startblock, ULONG blocks, ULONG *bestkey) {
6236 struct CacheBuffer *cb;
6237 struct fsExtentBNode *ebn;
6238 LONG maxscan=globals->defrag_maxfilestoscan;
6239 LONG errorcode;
6241 /* This function looks for the start of a ExtentBNode chain
6242 which matches the given size. If none is found, then the
6243 next smaller chain is returned. If none is found, then a
6244 larger chain is returned. If there are no chains at all
6245 0 is returned.
6247 The first ExtentBNode examined is determined by the start
6248 block number which is passed. */
6250 _DEBUG(("findmatch_fromend: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
6252 *bestkey=0;
6254 if(blocks>FRAGMENTS-1) {
6255 newfragmentinit_large(blocks);
6257 else {
6258 newfragmentinit_small(blocks);
6261 if((errorcode=lastbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0 && BE2L(ebn->be_key)>=startblock) {
6263 _DEBUG(("findmatch_fromend: ebn->key = %ld, ebn->blocks = %ld\n", BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
6265 do {
6266 if((BE2L(ebn->be_prev) & 0x80000000)!=0) { // Is this a 'first fragment' of something?
6267 struct CacheBuffer *cb2;
6268 struct fsObject *o;
6269 ULONG newblocks;
6271 _DEBUG(("findmatch_fromend!: ebn->key = %ld, ebn->blocks = %ld\n", BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
6273 /* Found the start of a candidate chain. */
6275 lockcachebuffer(cb);
6276 errorcode=readobject(BE2L(ebn->be_prev) & 0x7FFFFFFF, &cb2, &o);
6277 unlockcachebuffer(cb);
6279 if(errorcode!=0) {
6280 break;
6283 newblocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1)>>globals->shifts_block;
6285 if(blocks>FRAGMENTS-1) {
6286 *bestkey=newfragment_large(BE2L(ebn->be_key), newblocks);
6288 else {
6289 *bestkey=newfragment_small(BE2L(ebn->be_key), newblocks);
6292 if(*bestkey!=0) {
6293 return(0);
6296 if(--maxscan<0) {
6297 break;
6300 } while((errorcode=previousbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0 && BE2L(ebn->be_key)>=startblock);
6302 if(errorcode==0) {
6303 if(blocks>FRAGMENTS-1) {
6304 *bestkey=newfragmentend_large();
6306 else {
6307 *bestkey=newfragmentend_small();
6310 else {
6311 _DEBUG(("findmatch_fromend: errorcode=%ld\n", errorcode));
6315 return(errorcode);
6320 LONG step(void) {
6321 LONG errorcode;
6322 LONG free;
6324 if((free=availablespace(globals->block_defragptr, 256))!=-1) {
6326 _DEBUG(("Defragmenter: Found %ld blocks of free space at block %ld.\n", free, globals->block_defragptr));
6328 if(free==0) {
6329 struct CacheBuffer *cb;
6330 struct fsExtentBNode *ebn;
6332 /* Determine in which extent block_defragptr is located. */
6334 if((errorcode=findbnode(globals->block_extentbnoderoot, globals->block_defragptr, &cb, (struct BNode **)&ebn))==0) {
6335 if(ebn==0 || BE2L(ebn->be_key)!=globals->block_defragptr) {
6336 LONG block;
6338 _DEBUG(("Defragmenter: Found unmoveable data at block %ld.\n", globals->block_defragptr));
6340 /* Skip unmoveable data */
6342 if((block=skipunmoveable(globals->block_defragptr))!=-1) {
6343 globals->block_defragptr=block;
6345 else {
6346 errorcode=INTERR_DEFRAGMENTER;
6349 else if((BE2L(ebn->be_prev) & 0x80000000)!=0 || BE2L(ebn->be_prev)<globals->block_defragptr) {
6351 _DEBUG(("Defragmenter: Found a (partially) defragmented extent at block %ld.\n", globals->block_defragptr));
6353 if(BE2L(ebn->be_next)==0 || BE2L(ebn->be_next) == BE2L(ebn->be_key)+BE2W(ebn->be_blocks)) {
6354 /* If there is no next Extent, or if the next Extent is touching
6355 this one, then skip the current one. */
6357 _DEBUG(("Defragmenter: Extent has no next or next is touching this one.\n"));
6359 globals->block_defragptr+=BE2W(ebn->be_blocks);
6361 else {
6362 LONG freeafter;
6363 BLCK key=BE2L(ebn->be_key);
6364 BLCK next=BE2L(ebn->be_next);
6365 UWORD blocks=BE2W(ebn->be_blocks);
6367 _DEBUG(("Defragmenter: Extent has a next extent.\n"));
6369 if((freeafter=availablespace(key+blocks, 256))!=-1) {
6371 _DEBUG(("Defragmenter: There are %ld blocks of free space after the extent at block %ld.\n", freeafter, key));
6373 if(freeafter>0) {
6374 /* Move (part of) data located in next extent to this free space. */
6376 _DEBUG(("Defragmenter: Filling the gap.\n"));
6378 /* The function below can be called multiple times in a row. When there is a large
6379 gap in which multiple extents of the file will fit, then these can all be transfered
6380 into the gap at once. */
6382 errorcode=fillgap(key);
6384 else {
6385 LONG block;
6387 /* Determine which extent it is which is located directly after the current extent. */
6389 if((block=skipunmoveable(key+blocks))!=-1) {
6390 if(block==key+blocks) {
6392 _DEBUG(("Defragmenter: There was a moveable extent after the extent at block %ld.\n", key));
6394 /* There was no unmoveable data, so let's move it. */
6396 errorcode=makefreespace(block);
6398 else {
6399 LONG freeafter;
6401 _DEBUG(("Defragmenter: Skipped %ld blocks of unmoveable data.\n", block-(key+blocks)));
6403 /* Unmoveable data was skipped. */
6405 if(block!=next) { // Mar 20 1999: Check to see if the extent after the unmoveable data is not already the correct one.
6407 if((freeafter=availablespace(block, 256))!=-1) {
6409 _DEBUG(("Defragmenter: There are %ld blocks of free space after the unmoveable data.\n", freeafter));
6411 if(freeafter==0) {
6413 _DEBUG(("Defragmenter: Clearing some space at block %ld.\n", block));
6415 if((errorcode=makefreespace(block))==0) {
6416 if((freeafter=availablespace(block, 256))==-1) {
6417 errorcode=INTERR_DEFRAGMENTER;
6420 _DEBUG(("Defragmenter: There are now %ld blocks of cleared space after the unmoveable data.\n", freeafter));
6424 if(errorcode==0) {
6425 struct CacheBuffer *cb;
6426 struct fsExtentBNode *ebn;
6428 if((errorcode=findextentbnode(next, &cb, &ebn))==0) {
6430 _DEBUG(("Defragmenter: Moved next extent of our extent directly after the unmoveable space (block = %ld).\n", block));
6432 if((errorcode=moveextent(ebn, block, MIN(freeafter, BE2W(ebn->be_blocks))))==0) {
6433 globals->block_defragptr=block;
6438 else {
6439 errorcode=INTERR_DEFRAGMENTER;
6442 else {
6444 /* Extent after unmoveable data is already correct, so no need to move it. */
6446 globals->block_defragptr=block;
6450 else {
6451 errorcode=INTERR_DEFRAGMENTER;
6455 else {
6456 errorcode=INTERR_DEFRAGMENTER;
6460 else {
6462 _DEBUG(("Defragmenter: Found an extent at block %ld which must be moved away.\n", globals->block_defragptr));
6464 errorcode=makefreespace(globals->block_defragptr);
6468 else {
6469 ULONG bestkey;
6471 if((errorcode=findmatch_fromend(globals->block_defragptr, free, &bestkey))==0) {
6472 if(bestkey!=0) {
6473 struct CacheBuffer *cb;
6474 struct fsExtentBNode *ebn;
6476 if((errorcode=findextentbnode(bestkey, &cb, &ebn))==0) {
6478 _DEBUG(("Defragmenter: Moving a new first Extent to %ld\n", globals->block_defragptr));
6480 errorcode=moveextent(ebn, globals->block_defragptr, MIN(free, BE2W(ebn->be_blocks)));
6483 else {
6484 _DEBUG(("Defragmenter: Nothing more to optimize.\n"));
6486 add_done();
6491 else {
6492 errorcode=INTERR_DEFRAGMENTER;
6495 _DEBUG(("Defragmenter: Exiting with errorcode %ld\n\n",errorcode));
6497 return(errorcode);
6504 if (block at block_optimizeptr is full) {
6506 determine in which extent block_optimizeptr is located.
6508 if (not located in extent) {
6509 skip unmoveable data: add 1 to block_optimizeptr.
6511 else if (extent is first extent OR previous extent is located before block_optimizeptr) {
6512 if (extent is last extent (as well)) {
6513 file was defragmented: set block_optimizeptr to point just after this extent.
6515 else {
6516 determine if there is free space located after this extent.
6518 if (there is free space) {
6519 move (part of) data located in next extent to this free space.
6521 else {
6523 determine which extent it is which is located directly after the current extent.
6525 if (no such extent exists) {
6526 skip unmoveable data
6528 if (no free space after unmoveable data) {
6529 make some free space by moving data.
6531 move (part of) data located in next extent to the (newly made) free space: set block_optimizeptr in this part.
6533 else {
6534 make some free space first by moving data.
6539 else {
6540 make some free space by moving data at block_optimizeptr.
6543 else {
6545 determine amount of free space.
6547 look for an extent-chain equal to the amount of free space beyond this location.
6549 if (found suitable extent-chain) {
6550 move data to block_optimizeptr; set block_optimizeptr to point just after this data.
6552 else {
6553 scan for ANY 'first' extent beyond this location.
6555 if (none found) {
6556 optimization is done.
6558 else {
6559 move found 'first' extent to block_optimizeptr.
6568 /* The simple purpose of the SFS DosList handler task is to
6569 provide the following non-blocking functions:
6571 - Adding a VolumeNode to the DosList
6572 data = VolumeNode
6574 - Removing a VolumeNode from the DosList (synchronously)
6575 data = VolumeNode
6577 All messages sent to the DosList handler are freed by
6578 the DosList handler itself. This is to avoid having to
6579 wait for the reply and then free the message yourself.
6582 #ifdef __AROS__
6583 #undef globals
6584 #else
6585 #undef SysBase
6586 #endif
6587 #undef DOSBase
6589 static void sdlhtask(void)
6591 #ifndef __AROS__
6592 struct ExecBase *SysBase=globals->sysBase;
6593 #endif
6594 struct DosLibrary *DOSBase;
6596 if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37))!=0) {
6597 Forbid();
6598 if(FindPort("SFS DosList handler")==0) {
6599 struct MsgPort *port;
6601 if((port=CreateMsgPort())!=0) {
6602 struct SFSMessage *sfsm;
6604 port->mp_Node.ln_Name="SFS DosList handler";
6605 port->mp_Node.ln_Pri=1;
6606 AddPort(port);
6607 Permit();
6609 for(;;) {
6610 struct DosList *dol;
6612 WaitPort(port);
6614 dol=LockDosList(LDF_WRITE|LDF_VOLUMES);
6616 while((sfsm=(struct SFSMessage *)GetMsg(port))!=0) {
6617 if(sfsm->command==SFSM_ADD_VOLUMENODE) {
6618 /* AddDosEntry rejects volumes based on their name and date. */
6620 if(AddDosEntry((struct DosList *)sfsm->data)==DOSFALSE) {
6621 sfsm->errorcode=IoErr();
6624 // if(AddDosEntry((struct DosList *)sfsm->data)==DOSFALSE) {
6625 // errorcode=IoErr();
6626 // FreeDosEntry((struct DosList *)vn);
6627 // vn=0;
6628 // }
6630 else if(sfsm->command==SFSM_REMOVE_VOLUMENODE) {
6631 struct DosList *vn=(struct DosList *)sfsm->data;
6633 while((dol=NextDosEntry(dol, LDF_VOLUMES))!=0) {
6634 if(dol==vn) {
6635 RemDosEntry(dol);
6636 break;
6639 // removevolumenode(dol, (struct DosList *)sfsm->data); /* Dangerous because of DOSBase?? */
6640 FreeDosEntry(vn);
6643 FreeVec(sfsm);
6646 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
6649 else {
6650 Permit();
6653 else {
6654 Permit();
6657 CloseLibrary((struct Library *)DOSBase);