Switched debug output to KPrintF(), because format specifiers come in RawDoFmt()...
[AROS.git] / rom / filesys / SFS / FS / filesystemmain.c
blob0996e194852620d8aa8074fda1a4500270852cc8
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 TIMEOUT (1) /* Timeout in seconds */
76 #define FLUSHTIMEOUT (20) /* Flush timeout in seconds */
78 #define ID_BUSY AROS_LONG2BE(MAKE_ID('B','U','S','Y'))
80 /* Our own usage of NotifyRequest private data */
81 #define nr_Next nr_Reserved[2]
82 #define nr_Prev nr_Reserved[3]
84 /* structs */
85 #define SFSM_ADD_VOLUMENODE (1)
86 #define SFSM_REMOVE_VOLUMENODE (2)
88 struct SFSMessage {
89 struct Message msg;
90 ULONG command;
91 IPTR data;
92 LONG errorcode;
95 struct DefragmentStep {
96 ULONG id; // id of the step ("MOVE", "DONE" or 0)
97 ULONG length; // length in longwords (can be 0)
98 ULONG data[0]; // size of this array is determined by length.
102 /* global variables */
104 #include "globals.h"
106 #ifndef __AROS__
107 struct SFSBase *globals=NULL;
108 #endif
110 void initGlobals()
112 globals->is_LittleEndian = FALSE;
113 globals->inhibitnestcounter = 0;
114 globals->block_defragptr = 2;
115 globals->is_casesensitive = FALSE;
116 globals->has_recycled = TRUE;
117 globals->locklist = NULL;
118 globals->notifyrequests = NULL;
119 globals->activitytimeractive = FALSE;
120 globals->pendingchanges = FALSE;
121 globals->timerreset = FALSE;
122 globals->max_name_length = MAX_NAME_LENGTH;
123 globals->activity_timeout = FLUSHTIMEOUT;
124 globals->inactivity_timeout = TIMEOUT;
125 globals->retries = MAX_RETRIES;
126 globals->scsidirect = FALSE;
127 globals->does64bit = FALSE;
128 globals->newstyledevice = FALSE;
129 globals->deviceopened = FALSE;
130 globals->msgport = NULL;
131 globals->ioreq = NULL;
132 globals->ioreq2 = NULL;
133 globals->ioreqchangeint = NULL;
134 globals->cmdread = CMD_READ;
135 globals->cmdwrite = CMD_WRITE;
136 globals->blocks_maxtransfer = 1048576;
137 globals->mask_mask = -1;
138 globals->bufmemtype = MEMF_PUBLIC;
139 globals->transactionpool = 0;
140 globals->compressbuffer = 0;
141 globals->transactionnestcount = 0;
142 globals->iocache_lruhead = NULL;
143 globals->iocache_lines = 8;
144 globals->iocache_copyback = TRUE;
145 globals->iocache_readonwrite = FALSE;
146 globals->templockedobjectnode = 0;
147 globals->internalrename = FALSE;
148 globals->defrag_maxfilestoscan = 512;
149 globals->debugreqs=TRUE;
151 globals->mask_debug = 0xffff;
154 /* Prototypes */
156 static struct DosPacket *getpacket(struct Process *);
157 static struct DosPacket *waitpacket(struct Process *);
158 static void returnpacket(SIPTR,LONG);
159 static void sdlhtask(void);
161 /* Prototypes of cachebuffer related functions */
163 LONG readcachebuffercheck(struct CacheBuffer **,ULONG,ULONG);
164 void outputcachebuffer(struct CacheBuffer *cb);
166 /* Prototypes of node related functions */
168 LONG deleteextents(ULONG key);
169 static LONG findextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode);
170 static LONG createextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode);
172 /* Prototypes of debug functions */
174 ULONG calcchecksum(void);
175 void checksum(void);
177 /* Misc prototypes */
179 void starttimeout(void);
180 LONG flushcaches(void);
181 void invalidatecaches(void);
183 BOOL freeupspace(void);
185 BOOL checkchecksum(struct CacheBuffer *);
186 void setchecksum(struct CacheBuffer *);
188 void checknotifyforobject(struct CacheBuffer *cb,struct fsObject *o,UBYTE notifyparent);
189 void checknotifyforpath(UBYTE *path,UBYTE notifyparent);
190 void notify(struct NotifyRequest *nr);
191 UBYTE *fullpath(struct CacheBuffer *cbstart,struct fsObject *o);
193 LONG initdisk(void);
194 static void deinitdisk(void);
196 LONG handlesimplepackets(struct DosPacket *packet);
197 static LONG dumppackets(struct DosPacket *packet,LONG);
198 #ifdef DEBUGCODE
199 static void dumppacket(void);
200 #endif
201 static void actioncurrentvolume(struct DosPacket *);
202 static void actionsamelock(struct DosPacket *);
203 static void actiondiskinfo(struct DosPacket *);
204 static void fillinfodata(struct InfoData *);
205 static void fillfib(struct FileInfoBlock *,struct fsObject *);
206 static void diskchangenotify(ULONG class);
208 /* Prototypes of high-level filesystem functions */
210 LONG setfilesize(struct ExtFileLock *lock,ULONG bytes);
211 static LONG seek(struct ExtFileLock *lock,ULONG offset);
212 LONG seektocurrent(struct ExtFileLock *lock);
213 LONG seekextent(struct ExtFileLock *lock,ULONG offset,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_ebn,ULONG *returned_extentoffset);
214 void seekforward(struct ExtFileLock *lock, UWORD ebn_blocks, BLCK ebn_next, ULONG bytestoseek);
215 LONG writetofile(struct ExtFileLock *lock, UBYTE *buffer, ULONG bytestowrite);
217 static LONG extendblocksinfile(struct ExtFileLock *lock,ULONG blocks);
218 static LONG addblocks(UWORD blocks, BLCK newspace, NODE objectnode, BLCK *io_lastextentbnode);
219 LONG deletefileslowly(struct CacheBuffer *cbobject, struct fsObject *o);
221 void mainloop(void);
223 /* ASM prototypes */
225 #define MAJOR_VERSION (1)
226 #define MINOR_VERSION (84)
228 #ifdef __GNUC__
229 const char ver_version[]="\0$VER: " PROGRAMNAMEVER " 1.84 (" ADATE ")\r\n";
230 #else
231 static const char ver_version[]={"\0$VER: " PROGRAMNAMEVER " 1.84 " __AMIGADATE__ "\r\n"};
232 #endif
234 #ifdef __AROS__
235 /* AROS builds in a 'struct Resident' automatically
237 #else
238 /* ROMTag is useful for C:Version. */
239 #define res_Init NULL
241 const struct Resident resident =
243 RTC_MATCHWORD,
244 &resident,
245 (APTR)&resident + sizeof(struct Resident),
246 RTF_COLDSTART,
247 MAJOR_VERSION,
249 -81,
250 PROGRAMNAME,
251 &ver_version[7],
252 res_Init
254 #endif
256 /* Main */
258 #ifndef __AROS__
259 extern const RESBASE;
260 extern const RESLEN;
261 extern const _LinkerDB;
262 extern const NEWDATAL;
263 #endif
265 LONG mainprogram(struct ExecBase *);
267 #ifndef __AROS__
268 #undef SysBase
269 #endif
271 #ifndef __AROS__
272 LONG __saveds trampoline(void)
274 struct ExecBase *sBase = (*((struct ExecBase **)4));
276 return mainprogram(sBase);
279 LONG start(void)
281 return(STACKSWAP(4096, trampoline));
282 /* if(STACKSWAP()==0) {
283 return(ERROR_NO_FREE_STORE);
286 return(mainprogram()); */
288 #endif
290 void request2(UBYTE *text);
292 // #define STARTDEBUG
294 LONG mainprogram(struct ExecBase *SysBase)
296 #ifndef __AROS__
297 ULONG reslen;
298 APTR old_a4;
299 APTR newdata;
300 #endif
302 D(bug("[SFS] Filesystem main\n"));
304 globals = AllocMem(sizeof(struct SFSBase), MEMF_PUBLIC | MEMF_CLEAR);
305 #ifndef __AROS__
306 globals->sysBase = SysBase;
307 #endif
308 initGlobals();
310 #ifndef __AROS__
311 #undef SysBase
312 #define SysBase (globals->sysBase)
314 old_a4=(APTR)getreg(REG_A4);
315 reslen=((ULONG)&RESLEN-(ULONG)old_a4)+64;
317 newdata=AllocMem(reslen,MEMF_CLEAR|MEMF_PUBLIC);
319 CopyMem(old_a4,newdata,*(((ULONG *)old_a4)-2));
321 putreg(REG_A4,(LONG)newdata);
322 #endif
324 if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37))!=0) {
325 D(bug("[SFS] DOSBase = %p\n", DOSBase));
327 globals->mytask=(struct Process *)FindTask(0);
328 D(bug("[SFS] mytask = %p\n", globals->mytask));
330 globals->packet=waitpacket(globals->mytask);
331 D(bug("[SFS] packet = %p\n", globals->packet));
333 globals->devnode=(struct DeviceNode *)BADDR(globals->packet->dp_Arg3);
334 globals->devnode->dn_Task=&globals->mytask->pr_MsgPort;
335 globals->startupmsg=BADDR(globals->devnode->dn_Startup);
336 D(bug("[SFS] devnode = %p\n", globals->devnode));
337 D(bug("[SFS] startupmsg = %p\n", globals->startupmsg));
339 if(initcachebuffers()==0) {
341 if((IntuitionBase=(APTR)OpenLibrary("intuition.library",37))!=0) {
343 #ifdef STARTDEBUG
344 dreq("(1) Filesystem initializing...");
345 #endif
347 if((UtilityBase=(APTR)OpenLibrary("utility.library",37))!=0) {
349 /* Create a msgport and iorequest for opening timer.device */
351 if((globals->msgportnotify=CreateMsgPort())!=0) {
352 if((globals->msgporttimer=CreateMsgPort())!=0) {
353 if((globals->msgportflushtimer=CreateMsgPort())!=0) {
354 if((globals->inactivitytimer_ioreq=(struct timerequest *)CreateIORequest(globals->msgporttimer, sizeof(struct timerequest)))!=0) {
355 if((globals->activitytimer_ioreq=(struct timerequest *)CreateIORequest(globals->msgportflushtimer, sizeof(struct timerequest)))!=0) {
357 #ifdef STARTDEBUG
358 dreq("(2) Message ports and iorequests created");
359 #endif
361 if(OpenDevice("timer.device",UNIT_VBLANK,&globals->inactivitytimer_ioreq->tr_node,0)==0) {
362 if(OpenDevice("timer.device",UNIT_VBLANK,&globals->activitytimer_ioreq->tr_node,0)==0) {
364 globals->dosenvec=(struct DosEnvec *)BADDR(globals->startupmsg->fssm_Environ);
366 globals->timerBase=(struct Device *)globals->inactivitytimer_ioreq->tr_node.io_Device;
368 /* Create a msgport and iorequest for opening the filesystem device */
370 initlist((struct List *)&globals->globalhandles);
372 #ifdef STARTDEBUG
373 dreq("(3) Timer.device opened");
374 #endif
376 if(initcachedio(AROS_BSTR_ADDR(globals->startupmsg->fssm_Device), globals->startupmsg->fssm_Unit, globals->startupmsg->fssm_Flags, globals->dosenvec)==0) {
377 #ifdef STARTDEBUG
378 dreq("(4) Cached IO layer started");
379 #endif
381 globals->shifts_block32=globals->shifts_block-BLCKFACCURACY;
383 globals->mask_block32=(1<<globals->shifts_block32)-1;
384 globals->blocks_inbitmap=(globals->bytes_block-sizeof(struct fsBitmap))<<3; /* must be a multiple of 32 !! */
385 globals->blocks_bitmap=(globals->blocks_total+globals->blocks_inbitmap-1)/globals->blocks_inbitmap;
386 globals->blocks_admin=32;
388 globals->blocks_reserved_start=MAX(globals->dosenvec->de_Reserved,1);
389 globals->blocks_reserved_end=MAX(globals->dosenvec->de_PreAlloc,1);
392 ULONG blocks512, reserve;
394 blocks512=globals->blocks_total<<(globals->shifts_block-9);
395 reserve=SQRT(blocks512);
396 reserve=(reserve<<2) + reserve;
398 if(reserve > blocks512/100) { // Do not use more than 1% of the disk.
399 reserve = blocks512/100;
402 if(reserve < globals->blocks_admin) { // Use atleast 32 blocks, even if it is more than 1%.
403 reserve = globals->blocks_admin;
404 if(reserve > globals->blocks_total>>1) {
405 reserve = 0;
409 globals->block_rovingblockptr=globals->blocks_reserved_start + globals->blocks_admin + globals->blocks_bitmap + reserve;
411 _DEBUG(("RovingBlockPtr = %ld, reserve = %ld\n", globals->block_rovingblockptr, reserve));
413 // block_rovingblockptr=0;
416 globals->mask_debug=0x00000000;
418 globals->node_containers=(globals->bytes_block-sizeof(struct fsNodeContainer))/sizeof(BLCKn);
420 addchangeint((struct Task *)globals->mytask, 1<<globals->mytask->pr_MsgPort.mp_SigBit);
422 _DEBUG(("Initializing transactions\n"));
424 if(inittransactions()==0) {
426 #ifdef STARTDEBUG
427 dreq("(5) Transaction layer started");
428 #endif
430 if(addcachebuffers(globals->dosenvec->de_NumBuffers)==0) {
432 #ifdef STARTDEBUG
433 dreq("(6) Filesystem started succesfully!");
434 #endif
436 /* return startup-packet, the handler runs now */
438 _DEBUG(("Filesystem started! Volumenode = %ld\n",globals->volumenode));
439 _DEBUG(("Mountlist entry says: Allocate %ld buffers of memtype 0x%08lx\n",globals->dosenvec->de_NumBuffers,globals->dosenvec->de_BufMemType));
441 // returnpacket(DOSTRUE,0); // Sep 19 1999: Moved down again.
443 _DEBUG(("CreateNewProc..."));
445 const struct TagItem tags[]=
447 {NP_Entry , (IPTR)sdlhtask },
448 {NP_Name , (IPTR)"SFS DosList handler"},
449 {NP_Priority , 19 },
450 {TAG_DONE , 0 }
453 if(CreateNewProc(tags)!=0) {
455 _DEBUG(("ok\n"));
457 while((globals->sdlhport=FindPort("SFS DosList handler"))==0) {
458 Delay(2);
461 if(isdiskpresent()!=FALSE) {
462 #ifdef STARTDEBUG
463 dreq("There is a disk present.");
464 #endif
466 initdisk();
468 else {
469 #ifdef STARTDEBUG
470 dreq("No disk inserted.");
471 #endif
473 globals->disktype=ID_NO_DISK_PRESENT;
476 returnpacket(DOSTRUE,0); // Jul 4 1999: Moved up...
478 #ifdef STARTDEBUG
479 dreq("(7) Informed DOS about the new partition!");
480 #endif
482 mainloop();
486 cleanuptransactions();
489 removechangeint();
490 cleanupcachedio();
492 CloseDevice(&globals->activitytimer_ioreq->tr_node);
494 CloseDevice(&globals->inactivitytimer_ioreq->tr_node);
496 DeleteIORequest((struct IORequest *)globals->activitytimer_ioreq);
498 DeleteIORequest((struct IORequest *)globals->inactivitytimer_ioreq);
500 DeleteMsgPort(globals->msgportflushtimer);
502 DeleteMsgPort(globals->msgporttimer);
504 DeleteMsgPort(globals->msgportnotify);
506 CloseLibrary((struct Library *)UtilityBase);
509 #ifdef STARTDEBUG
510 dreq("Filesystem failed.. exiting.");
511 #endif
513 CloseLibrary((struct Library *)IntuitionBase);
517 _DEBUG(("Returning startup packet with DOSFALSE\n"));
519 returnpacket(DOSFALSE,ERROR_NO_FREE_STORE);
521 CloseLibrary((struct Library *)DOSBase);
524 FreeMem(globals, sizeof(struct SFSBase));
525 _DEBUG(("Exiting filesystem\n"));
527 return(ERROR_NO_FREE_STORE);
531 void mainloop(void) {
532 ULONG signalbits;
533 struct MsgPort *msgportpackets;
534 struct Message *msg;
536 msgportpackets=&globals->mytask->pr_MsgPort; /* get port of our process */
537 signalbits=1<<msgportpackets->mp_SigBit;
538 signalbits|=1<<globals->msgporttimer->mp_SigBit;
539 signalbits|=1<<globals->msgportnotify->mp_SigBit;
540 signalbits|=1<<globals->msgportflushtimer->mp_SigBit;
542 #ifdef STARTDEBUG
543 dreq("Entering packet loop.");
544 #endif
546 for(;;) {
548 Wait(signalbits);
550 do {
551 while((msg=GetMsg(globals->msgportflushtimer))!=0) {
552 _TDEBUG(("mainloop: activity timeout -> flushed transaction\n"));
553 flushtransaction();
554 globals->activitytimeractive=FALSE;
557 while((msg=GetMsg(globals->msgporttimer))!=0) {
558 if(globals->timerreset==TRUE) {
559 /* There was another request during the timeout/2 period, so we extend the timeout a bit longer. */
560 globals->pendingchanges=FALSE;
561 starttimeout();
563 else {
564 _TDEBUG(("mainloop: inactivity timeout -> flushed transaction\n"));
565 flushcaches();
566 globals->pendingchanges=FALSE;
570 while((msg=GetMsg(globals->msgportnotify))!=0) {
571 FreeMem(msg,sizeof(struct NotifyMessage));
574 if(getchange()!=0) {
576 /* The disk was inserted or removed! */
578 _DEBUG(("mainloop: disk inserted or removed\n"));
580 if(isdiskpresent()==FALSE) {
581 /* Disk was removed */
583 globals->disktype=ID_NO_DISK_PRESENT; /* Must be put before deinitdisk() */
584 deinitdisk();
586 else {
587 /* Disk was inserted */
589 initdisk();
593 if((msg=GetMsg(msgportpackets))!=0) {
596 // diskstate=writeprotection(); /* Don't do this too often!! It takes LOADS of time for scsi.device. */
599 #ifdef CHECKCODE_SLOW
601 struct CacheBuffer *cb;
603 cb=(struct CacheBuffer *)cblrulist.mlh_Head;
605 while(cb->node.mln_Succ!=0) {
606 if(cb->locked!=0) {
607 request(PROGRAMNAME " request","%s\n"\
608 "mainloop: There was a locked CacheBuffer (lockcount = %ld, block = %ld, type = 0x%08lx)!\n"\
609 "Nothing bad will happen, but let the author know.\n",
610 "Ok",AROS_BSTR_ADDR(devnode->dn_Name), cb->locked, cb->blckno, *((ULONG *)cb->data));
613 cb->locked=0; /* Nothing remains locked */
616 cb=(struct CacheBuffer *)(cb->node.mln_Succ);
619 #endif
622 globals->packet=(struct DosPacket *)msg->mn_Node.ln_Name;
624 #ifdef STARTDEBUG
625 dreq("Received packet 0x%08lx, %ld.",globals->packet,globals->packet->dp_Type);
626 #endif
628 switch(globals->packet->dp_Type) {
629 case ACTION_SFS_SET:
631 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
632 struct TagItem *tag;
634 while((tag=NextTagItem(&taglist))!=NULL) {
635 LONG data=tag->ti_Data;
637 switch(tag->ti_Tag) {
638 case ASS_MAX_NAME_LENGTH:
639 if(data >= 30 && data <= 100) {
640 globals->max_name_length=data;
642 break;
643 case ASS_ACTIVITY_FLUSH_TIMEOUT:
644 if(data >= 5 && data <= 120) {
645 globals->activity_timeout=data;
647 break;
648 case ASS_INACTIVITY_FLUSH_TIMEOUT:
649 if(data >= 1 && data <= 5) {
650 globals->inactivity_timeout=data;
652 break;
656 returnpacket(DOSTRUE,0);
658 break;
659 case ACTION_SFS_QUERY:
661 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
662 struct TagItem *tag;
664 while((tag=NextTagItem(&taglist)))
666 switch(tag->ti_Tag)
668 case ASQ_START_BYTEH:
669 tag->ti_Data = globals->byte_low >> 32;
670 break;
672 case ASQ_START_BYTEL:
674 * Explicitly cast to ULONG here because on 64 bits
675 * ti_Data is 64-bit wide, and this can confuse programs.
677 tag->ti_Data = (ULONG)globals->byte_low;
678 break;
680 case ASQ_END_BYTEH:
681 tag->ti_Data = globals->byte_high >> 32;
682 break;
684 case ASQ_END_BYTEL:
685 tag->ti_Data = (ULONG)globals->byte_high;
686 break;
688 case ASQ_DEVICE_API:
689 tag->ti_Data=deviceapiused();
690 break;
691 case ASQ_BLOCK_SIZE:
692 tag->ti_Data=globals->bytes_block;
693 break;
694 case ASQ_TOTAL_BLOCKS:
695 tag->ti_Data=globals->blocks_total;
696 break;
697 case ASQ_ROOTBLOCK:
698 tag->ti_Data=globals->block_root;
699 break;
700 case ASQ_ROOTBLOCK_OBJECTNODES:
701 tag->ti_Data=globals->block_objectnoderoot;
702 break;
703 case ASQ_ROOTBLOCK_EXTENTS:
704 tag->ti_Data=globals->block_extentbnoderoot;
705 break;
706 case ASQ_FIRST_BITMAP_BLOCK:
707 tag->ti_Data=globals->block_bitmapbase;
708 break;
709 case ASQ_FIRST_ADMINSPACE:
710 tag->ti_Data=globals->block_adminspace;
711 break;
712 case ASQ_CACHE_LINES:
713 tag->ti_Data=queryiocache_lines();
714 break;
715 case ASQ_CACHE_READAHEADSIZE:
716 tag->ti_Data=queryiocache_readaheadsize();
717 break;
718 case ASQ_CACHE_MODE:
719 tag->ti_Data=queryiocache_copyback();
720 break;
721 case ASQ_CACHE_BUFFERS:
722 tag->ti_Data=globals->totalbuffers;
723 break;
724 case ASQ_CACHE_ACCESSES:
725 tag->ti_Data=globals->statistics.cache_accesses;
726 break;
727 case ASQ_CACHE_MISSES:
728 tag->ti_Data=globals->statistics.cache_misses;
729 break;
730 case ASQ_OPERATIONS_DECODED:
731 tag->ti_Data=globals->statistics.cache_operationdecode;
732 break;
733 case ASQ_EMPTY_OPERATIONS_DECODED:
734 tag->ti_Data=globals->statistics.cache_emptyoperationdecode;
735 break;
736 case ASQ_IS_CASESENSITIVE:
737 tag->ti_Data=globals->is_casesensitive;
738 break;
739 case ASQ_HAS_RECYCLED:
740 tag->ti_Data=globals->has_recycled;
741 break;
742 case ASQ_VERSION:
743 tag->ti_Data=MAJOR_VERSION * 65536 + MINOR_VERSION;
744 break;
745 case ASQ_MAX_NAME_LENGTH:
746 tag->ti_Data=globals->max_name_length;
747 break;
748 case ASQ_ACTIVITY_FLUSH_TIMEOUT:
749 tag->ti_Data=globals->activity_timeout;
750 break;
751 case ASQ_INACTIVITY_FLUSH_TIMEOUT:
752 tag->ti_Data=globals->inactivity_timeout;
753 break;
757 returnpacket(DOSTRUE,0);
759 break;
760 case ACTION_SET_DEBUG:
761 _DEBUG(("New debug level set to 0x%08lx!\n",globals->packet->dp_Arg1));
763 globals->mask_debug=globals->packet->dp_Arg1;
765 returnpacket(DOSTRUE,0);
767 break;
768 case ACTION_SET_CACHE:
769 _DEBUG(("ACTION_SET_CACHE\n"));
771 LONG errorcode;
773 if((errorcode=setiocache(globals->packet->dp_Arg1, globals->packet->dp_Arg2, globals->packet->dp_Arg3 & 1))!=0) {
774 returnpacket(DOSFALSE, errorcode);
776 else {
777 returnpacket(DOSTRUE, 0);
781 break;
782 case ACTION_SFS_FORMAT:
783 case ACTION_FORMAT:
784 _DEBUG(("ACTION_FORMAT\n"));
787 struct CacheBuffer *cb;
788 ULONG currentdate;
789 BLCK block_recycled;
790 LONG errorcode=0;
792 UBYTE *name=0;
793 UBYTE *recycledname=".recycled";
794 BYTE casesensitive=FALSE;
795 BYTE norecycled=FALSE;
796 BYTE showrecycled=FALSE;
797 currentdate=getdate();
799 if(globals->packet->dp_Type==ACTION_SFS_FORMAT) {
800 struct TagItem *taglist=(struct TagItem *)globals->packet->dp_Arg1;
801 struct TagItem *tag;
803 while((tag=NextTagItem(&taglist))) {
804 switch(tag->ti_Tag) {
805 case ASF_NAME:
806 name=(UBYTE *)tag->ti_Data;
807 break;
808 case ASF_RECYCLEDNAME:
809 recycledname=(UBYTE *)tag->ti_Data;
810 break;
811 case ASF_CASESENSITIVE:
812 casesensitive=tag->ti_Data;
813 break;
814 case ASF_NORECYCLED:
815 norecycled=tag->ti_Data;
816 break;
817 case ASF_SHOWRECYCLED:
818 showrecycled=tag->ti_Data;
819 break;
823 else {
824 copybstrasstr((BSTR)globals->packet->dp_Arg1, globals->string, 30);
825 name=globals->string;
828 /* Global block numbers */
830 globals->block_adminspace=globals->blocks_reserved_start;
831 globals->block_root=globals->blocks_reserved_start+1;
832 globals->block_extentbnoderoot=globals->block_root+3;
833 globals->block_bitmapbase=globals->block_adminspace+globals->blocks_admin;
834 globals->block_objectnoderoot=globals->block_root+4;
836 /* Temporary block numbers */
838 block_recycled=globals->block_root+5;
840 cb=getcachebuffer();
842 if(isvalidcomponentname(name)==FALSE || isvalidcomponentname(recycledname)==FALSE) {
843 errorcode=ERROR_INVALID_COMPONENT_NAME;
846 if(errorcode==0) {
847 struct fsAdminSpaceContainer *ac=cb->data;
849 _DEBUG(("ACTION_FORMAT: Creating AdminSpace container block\n"));
851 /* Create AdminSpaceContainer block */
853 cb->blckno=globals->block_adminspace;
854 clearcachebuffer(cb);
856 ac->bheader.id=ADMINSPACECONTAINER_ID;
857 ac->bheader.be_ownblock=L2BE(globals->block_adminspace);
858 ac->bits=globals->blocks_admin;
859 ac->adminspace[0].be_space=L2BE(globals->block_adminspace);
861 if(norecycled==FALSE) {
862 /* NOT HASHED RECYCLED
863 ac->adminspace[0].be_bits=L2BE(0xFF000000); // admin + root + hashtable + restore + 2 * nodecontainers + recycled + hash
865 ac->adminspace[0].be_bits=L2BE(0xFE000000); /* admin + root + hashtable + restore + 2 * nodecontainers + recycled */
867 else {
868 ac->adminspace[0].be_bits=L2BE(0xFC000000); /* admin + root + hashtable + restore + 2 * nodecontainers */
871 setchecksum(cb);
872 errorcode=writecachebuffer(cb);
875 if(errorcode==0) {
876 struct fsObjectContainer *oc=cb->data;
877 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
879 _DEBUG(("ACTION_FORMAT: Creating Root block\n"));
881 /* Create Root block */
883 cb->blckno=globals->block_root;
884 clearcachebuffer(cb);
886 oc->bheader.id=OBJECTCONTAINER_ID;
887 oc->bheader.be_ownblock=L2BE(globals->block_root);
888 oc->object[0].be_protection=L2BE(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
889 oc->object[0].be_datemodified=L2BE(currentdate);
890 oc->object[0].bits=OTYPE_DIR;
891 oc->object[0].be_objectnode=L2BE(ROOTNODE);
893 oc->object[0].object.dir.be_hashtable=L2BE(globals->block_root+1);
895 if(norecycled==FALSE) {
896 oc->object[0].object.dir.be_firstdirblock=L2BE(block_recycled);
899 copystr(name, oc->object[0].name, 30);
901 ri->be_freeblocks=L2BE(globals->blocks_total-globals->blocks_admin-globals->blocks_reserved_start-globals->blocks_reserved_end-globals->blocks_bitmap);
902 ri->be_datecreated=oc->object[0].be_datemodified; // BE-BE copy
904 setchecksum(cb);
905 errorcode=writecachebuffer(cb);
908 if(errorcode==0) {
909 struct fsHashTable *ht=cb->data;
911 _DEBUG(("ACTION_FORMAT: Creating Root's HashTable block\n"));
913 /* Create Root's HashTable block */
915 cb->blckno=globals->block_root+1;
916 clearcachebuffer(cb);
918 ht->bheader.id=HASHTABLE_ID;
919 ht->bheader.be_ownblock=L2BE(globals->block_root+1);
920 ht->be_parent=L2BE(ROOTNODE);
922 if(norecycled==FALSE) {
923 ht->be_hashentry[HASHCHAIN(hash(".recycled", casesensitive))]=L2BE(RECYCLEDNODE);
926 setchecksum(cb);
927 errorcode=writecachebuffer(cb);
930 if(errorcode==0) {
931 struct fsBlockHeader *bh=cb->data;
933 _DEBUG(("ACTION_FORMAT: Creating Transaction block\n"));
935 /* Create empty block as a placeholder for the TransactionFailure block. */
937 cb->blckno=globals->block_root+2;
938 clearcachebuffer(cb);
940 bh->id=TRANSACTIONOK_ID;
941 bh->be_ownblock=L2BE(globals->block_root+2);
943 setchecksum(cb);
944 errorcode=writecachebuffer(cb);
947 if(errorcode==0) {
948 struct fsBNodeContainer *bnc=cb->data;
949 struct BTreeContainer *btc=&bnc->btc;
951 _DEBUG(("ACTION_FORMAT: Creating ExtentNode root block\n"));
953 /* Create NodeContainer block for ExtentNodes */
955 cb->blckno=globals->block_extentbnoderoot;
956 clearcachebuffer(cb);
958 bnc->bheader.id=BNODECONTAINER_ID;
959 bnc->bheader.be_ownblock=L2BE(globals->block_extentbnoderoot);
961 btc->isleaf=TRUE;
962 btc->be_nodecount=0;
963 btc->nodesize=sizeof(struct fsExtentBNode);
965 setchecksum(cb);
966 errorcode=writecachebuffer(cb);
969 if(errorcode==0) {
970 struct fsNodeContainer *nc=cb->data;
971 struct fsObjectNode *on;
973 _DEBUG(("ACTION_FORMAT: Creating ObjectNode root block\n"));
975 /* Create NodeContainer block for ObjectNodes */
977 cb->blckno=globals->block_objectnoderoot;
978 clearcachebuffer(cb);
980 nc->bheader.id=NODECONTAINER_ID;
981 nc->bheader.be_ownblock=L2BE(globals->block_objectnoderoot);
983 nc->be_nodenumber=L2BE(1); /* objectnode 0 is reserved :-) */
984 nc->be_nodes=L2BE(1);
986 on=(struct fsObjectNode *)nc->be_node;
987 on->node.be_data=L2BE(globals->block_root);
989 on++;
990 if(norecycled==FALSE) {
991 on->node.be_data=L2BE(block_recycled);
992 on->be_hash16=W2BE(hash(".recycled", casesensitive));
994 else {
995 on->node.be_data=-1; // reserved 2
998 on++;
999 on->node.be_data=-1; // reserved 3
1001 on++;
1002 on->node.be_data=-1; // reserved 4
1004 on++;
1005 on->node.be_data=-1; // reserved 5
1007 on++;
1008 on->node.be_data=-1; // reserved 6
1010 setchecksum(cb);
1011 errorcode=writecachebuffer(cb);
1014 if(errorcode==0 && norecycled==FALSE) {
1015 struct fsObjectContainer *oc=cb->data;
1017 _DEBUG(("ACTION_FORMAT: Creating Root ObjectContainer block\n"));
1019 cb->blckno=block_recycled;
1020 clearcachebuffer(cb);
1022 oc->bheader.id=OBJECTCONTAINER_ID;
1023 oc->bheader.be_ownblock=L2BE(block_recycled);
1024 oc->be_parent=L2BE(ROOTNODE);
1025 oc->object[0].be_protection=L2BE(FIBF_READ|FIBF_WRITE);
1026 oc->object[0].be_datemodified=L2BE(currentdate);
1027 oc->object[0].bits=OTYPE_DIR|OTYPE_UNDELETABLE|OTYPE_QUICKDIR;
1028 if(showrecycled==FALSE) {
1029 oc->object[0].bits|=OTYPE_HIDDEN;
1032 oc->object[0].be_objectnode=L2BE(RECYCLEDNODE);
1034 /* NOT HASHED RECYCLED
1035 oc->object[0].object.dir.hashtable=block_recycled+1;
1037 oc->object[0].object.dir.be_hashtable=0;
1039 copystr(".recycled",oc->object[0].name,30);
1041 setchecksum(cb);
1042 errorcode=writecachebuffer(cb);
1045 /* NOT HASHED RECYCLED
1046 if(errorcode==0 && norecycled==FALSE) {
1047 struct fsHashTable *ht=cb->data;
1049 _DEBUG(("ACTION_FORMAT: Creating Recycled's HashTable block\n"));
1051 cb->blckno=block_recycled+1;
1052 clearcachebuffer(cb);
1054 ht->bheader.id=HASHTABLE_ID;
1055 ht->bheader.ownblock=block_recycled+1;
1056 ht->parent=RECYCLEDNODE;
1058 setchecksum(cb);
1059 errorcode=writecachebuffer(cb);
1063 if(errorcode==0) {
1064 struct fsBitmap *bm;
1065 UWORD cnt,cnt2;
1066 ULONG block=globals->block_bitmapbase;
1067 LONG startfree=globals->blocks_admin+globals->blocks_bitmap+globals->blocks_reserved_start;
1068 LONG sizefree;
1070 _DEBUG(("ACTION_FORMAT: Creating the Bitmap blocks\n"));
1072 /* Create Bitmap blocks */
1074 sizefree=globals->blocks_total-startfree-globals->blocks_reserved_end;
1076 cnt=globals->blocks_bitmap;
1077 while(cnt-->0 && errorcode==0) {
1078 clearcachebuffer(cb);
1080 bm=cb->data;
1081 bm->bheader.id=BITMAP_ID;
1082 bm->bheader.be_ownblock=L2BE(block);
1084 for(cnt2=0; cnt2<(globals->blocks_inbitmap>>5); cnt2++) {
1085 if(startfree>0) {
1086 startfree-=32;
1087 if(startfree<0) {
1088 bm->bitmap[cnt2]=AROS_LONG2BE((1<<(-startfree))-1);
1089 sizefree+=startfree;
1092 else if(sizefree>0) {
1093 sizefree-=32;
1094 if(sizefree<0) {
1095 bm->bitmap[cnt2]=AROS_LONG2BE(~((1<<(-sizefree))-1));
1097 else {
1098 bm->bitmap[cnt2]=BITMAPFILL;
1101 else {
1102 break;
1106 cb->blckno=block++;
1108 setchecksum(cb);
1109 errorcode=writecachebuffer(cb);
1113 if(errorcode==0) {
1114 struct fsRootBlock *rb;
1116 _DEBUG(("ACTION_FORMAT: Creating the Root blocks\n"));
1118 /* Create Root blocks */
1120 cb->blckno=0;
1121 clearcachebuffer(cb);
1123 rb=cb->data;
1124 rb->bheader.id=L2BE(DOSTYPE_ID);
1125 rb->bheader.be_ownblock=0;
1127 rb->be_version=W2BE(STRUCTURE_VERSION);
1128 rb->be_sequencenumber=0;
1130 rb->be_datecreated=L2BE(currentdate);
1132 rb->be_firstbyteh = L2BE(globals->byte_low >> 32);
1133 rb->be_firstbyte = L2BE(globals->byte_low);
1134 rb->be_lastbyteh = L2BE(globals->byte_high >> 32);
1135 rb->be_lastbyte = L2BE(globals->byte_high);
1137 rb->be_totalblocks=L2BE(globals->blocks_total);
1138 rb->be_blocksize=L2BE(globals->bytes_block);
1140 rb->be_bitmapbase=L2BE(globals->block_bitmapbase);
1141 rb->be_adminspacecontainer=L2BE(globals->block_adminspace);
1142 rb->be_rootobjectcontainer=L2BE(globals->block_root);
1143 rb->be_extentbnoderoot=L2BE(globals->block_extentbnoderoot);
1144 rb->be_objectnoderoot=L2BE(globals->block_objectnoderoot);
1146 if(casesensitive!=FALSE) {
1147 rb->bits|=ROOTBITS_CASESENSITIVE;
1149 if(norecycled==FALSE) {
1150 rb->bits|=ROOTBITS_RECYCLED;
1153 setchecksum(cb);
1154 if((errorcode=writecachebuffer(cb))==0) {
1155 cb->blckno=globals->blocks_total-1;
1157 _DEBUG(("ACTION_FORMAT: Creating the 2nd Root block\n"));
1159 rb->bheader.be_ownblock=L2BE(globals->blocks_total-1);
1161 setchecksum(cb);
1162 errorcode=writecachebuffer(cb);
1166 flushiocache();
1167 update();
1168 motoroff();
1170 if(errorcode!=0) {
1171 _DEBUG(("ACTION_FORMAT: Exiting with errorcode %ld\n",errorcode));
1173 returnpacket(DOSFALSE, errorcode);
1175 else {
1176 returnpacket(DOSTRUE, 0);
1180 break;
1181 case ACTION_INHIBIT:
1182 _DEBUG(("ACTION_INHIBIT(%ld)\n",globals->packet->dp_Arg1));
1184 /* This function nests. Each call to inhibit the disk should be matched
1185 with one to uninhibit the disk. */
1187 if(globals->packet->dp_Arg1!=DOSFALSE) {
1188 #ifdef STARTDEBUG
1189 dreq("Disk inhibited (nesting = %ld).", globals->inhibitnestcounter);
1190 #endif
1192 if(globals->inhibitnestcounter++==0) { // Inhibited for the first time?
1193 globals->disktype=ID_BUSY; /* Must be put before deinitdisk() Feb 27 1999: Maybe not needed anymore */
1194 deinitdisk();
1197 returnpacket(DOSTRUE,0);
1199 else if(globals->inhibitnestcounter>0 && --globals->inhibitnestcounter==0) {
1201 returnpacket(DOSTRUE, 0); /* Workbench keeps doslist locked, and doesn't send any packets
1202 during that time to this handler. As initdisk() needs to lock
1203 the doslist we MUST return the packet before calling initdisk() */
1204 initdisk();
1206 else {
1207 /* Workbench revokes ACTION_INHIBIT without ever actually having
1208 inhibited the volume. We'll just return the packet and ignore
1209 such requests. */
1211 returnpacket(DOSTRUE,0);
1213 break;
1214 case ACTION_SERIALIZE_DISK:
1215 _DEBUG(("ACTION_SERIALIZE_DISK\n"));
1218 struct CacheBuffer *cb;
1219 LONG errorcode;
1221 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
1222 struct fsObjectContainer *oc=cb->data;
1223 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
1225 oc->object[0].be_datemodified=L2BE(getdate());
1226 ri->be_datecreated=oc->object[0].be_datemodified; // BE-BE copy
1228 setchecksum(cb);
1229 errorcode=writecachebuffer(cb);
1232 if(errorcode!=0) {
1233 returnpacket(DOSFALSE,errorcode);
1235 else {
1236 returnpacket(DOSTRUE,0);
1240 break;
1241 default:
1242 if(handlesimplepackets(globals->packet)==0) {
1244 if(globals->disktype == DOSTYPE_ID) {
1245 switch(globals->packet->dp_Type) {
1246 case ACTION_MAKE_LINK:
1247 _DEBUG(("ACTION_MAKE_LINK\n"));
1250 if(globals->packet->dp_Arg4==LINK_HARD) {
1251 returnpacket(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
1253 /* else if(packet->dp_Arg4!=LINK_SOFT) {
1254 returnpacket(DOSFALSE,ERROR_BAD_NUMBER);
1255 } */ /* Check removed because DOS apparantely defines non-zero as being a Soft link! */
1256 else {
1257 struct ExtFileLock *lock;
1258 LONG errorcode;
1260 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1261 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1263 _DEBUG(("ACTION_MAKE_LINK: Name = '%s', LinkPath = '%s'\n",globals->string,(UBYTE *)globals->packet->dp_Arg3));
1265 if((errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,(UBYTE *)globals->packet->dp_Arg3))!=0) {
1266 returnpacket(DOSFALSE,errorcode);
1268 else {
1269 // freelock(lock);
1270 returnpacket(DOSTRUE,0);
1275 break;
1276 case ACTION_READ_LINK:
1277 _DEBUG(("ACTION_READ_LINK\n"));
1280 struct CacheBuffer *cb;
1281 struct fsObject *o;
1282 struct ExtFileLock *lock;
1283 UBYTE *dest=(UBYTE *)globals->packet->dp_Arg3;
1284 LONG errorcode;
1285 NODE objectnode;
1287 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1289 if(lock==0) {
1290 objectnode=ROOTNODE;
1292 else {
1293 objectnode=lock->objectnode;
1296 if((errorcode=readobject(objectnode, &cb, &o))==0) {
1297 UBYTE *path=(UBYTE *)globals->packet->dp_Arg2, *prefix=path;
1299 _DEBUG(("ACTION_READ_LINK: path = '%s', errorcode = %ld\n",path,errorcode));
1301 errorcode=locateobject2(&path, &cb, &o);
1303 if(errorcode!=ERROR_IS_SOFT_LINK) {
1304 errorcode=ERROR_OBJECT_NOT_FOUND;
1306 else {
1307 struct CacheBuffer *cb2;
1308 UBYTE *p=path;
1310 /* Move on to remainder of path after the link */
1312 while(*path!=0) {
1313 if(*path=='/') {
1314 break;
1316 path++;
1319 _DEBUG(("ACTION_READ_LINK: path = '%s'\n",path));
1321 if((errorcode=readcachebuffercheck(&cb2, BE2L(o->object.file.be_data), SOFTLINK_ID))==0) {
1322 struct fsSoftLink *sl=cb2->data;
1323 LONG length=globals->packet->dp_Arg4;
1324 UBYTE *src=sl->string, ch;
1325 UBYTE *s=globals->string;
1327 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)
1330 s=globals->string;
1332 _DEBUG(("ACTION_READ_LINK: length = %ld, sl->string = '%s', path = '%s'\n", length, sl->string, s));
1334 /* cb is no longer valid at this point. */
1336 /* Check if link target is an absolute path */
1338 while((ch=*src++)!='\0') {
1339 if(ch==':')
1340 break;
1342 src=sl->string;
1344 /* If target is a relative path, put path preceding
1345 link into buffer so that result is relative to
1346 lock passed in */
1348 if(ch!=':') {
1349 while(prefix!=p && length-->0) {
1350 *dest++=*prefix++;
1354 /* Copy link target to buffer */
1356 while(length-->0 && (*dest++=*src++)!=0) {
1358 dest--;
1359 length++;
1361 /* Ensure we don't insert an extraneous slash */
1363 if(length!=globals->packet->dp_Arg4 && *(dest-1)=='/') {
1364 *--dest='\0';
1365 length++;
1367 if(*(dest-1)==':' && *s=='/') {
1368 s++;
1371 /* Append remainder of original path */
1373 while(length-->0 && (*dest++=*s++)!=0) {
1376 if(length<0) {
1377 errorcode=ERROR_LINE_TOO_LONG;
1383 if(errorcode!=0) {
1384 if(errorcode==ERROR_LINE_TOO_LONG) {
1385 returnpacket(-2, errorcode);
1387 else {
1388 returnpacket(-1, errorcode);
1391 else {
1392 returnpacket((LONG)((SIPTR)dest - globals->packet->dp_Arg3 - 1),
1396 break;
1397 case ACTION_CREATE_DIR:
1398 _DEBUG(("ACTION_CREATE_DIR\n"));
1401 struct ExtFileLock *lock;
1402 LONG errorcode;
1404 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
1405 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1407 if((errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,0))!=0) {
1408 returnpacket(0,errorcode);
1410 else {
1411 returnpacket((SIPTR)TOBADDR(lock),0);
1415 break;
1416 case ACTION_FINDUPDATE:
1417 case ACTION_FINDINPUT:
1418 case ACTION_FINDOUTPUT:
1419 case ACTION_FH_FROM_LOCK:
1420 _XDEBUG((DEBUG_OBJECTS, "ACTION_FIND#? or ACTION_FH_FROM_LOCK\n"));
1423 struct ExtFileLock *lock;
1424 struct FileHandle *fh;
1425 LONG errorcode=0;
1427 fh=(struct FileHandle *)BADDR(globals->packet->dp_Arg1);
1428 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg2);
1430 if(globals->packet->dp_Type!=ACTION_FH_FROM_LOCK) {
1431 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1432 _DEBUG(("OPEN FILE: %s (mode = %ld)\n", globals->string, globals->packet->dp_Type));
1433 errorcode=findcreate(&lock,globals->string,globals->packet->dp_Type,0);
1436 if(errorcode==0) {
1437 if((errorcode=createglobalhandle(lock))==0) {
1438 fh->fh_Arg1=(IPTR)lock;
1440 else if(globals->packet->dp_Type!=ACTION_FH_FROM_LOCK) {
1441 freelock(lock);
1445 if(errorcode!=0) {
1446 returnpacket(DOSFALSE,errorcode);
1448 else {
1449 if(globals->packet->dp_Type==ACTION_FINDOUTPUT && globals->has_recycled!=FALSE) {
1450 ULONG files,blocks;
1452 if((errorcode=getrecycledinfo(&files, &blocks))==0) {
1453 if(files>35) {
1454 cleanupdeletedfiles();
1459 returnpacket(DOSTRUE,0);
1463 break;
1464 case ACTION_END:
1465 _XDEBUG((DEBUG_OBJECTS, "ACTION_END\n"));
1467 /* ACTION_FREE_LOCK's code is similair */
1470 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1471 LONG errorcode;
1473 /* If a file has been modified by the use of ACTION_SET_FILE_SIZE or
1474 ACTION_WRITE, or ACTION_FINDOUTPUT or ACTION_FINDUPDATE (only when
1475 creating a new file) were used to create the file then we must
1476 also update the datestamp of the file. We also must send out a
1477 notification. Finally we also need to clear the A bit.
1479 For this purpose a special flag in the lock tells us
1480 whether or not any of the above actions has occured. */
1482 if((lock->bits & EFL_MODIFIED) != 0) {
1483 struct CacheBuffer *cb;
1484 struct fsObject *o;
1486 /* Aha! */
1488 if(lock->lastextendedblock!=0) {
1489 globals->block_rovingblockptr=lock->lastextendedblock;
1490 if(globals->block_rovingblockptr>=globals->blocks_total) {
1491 globals->block_rovingblockptr=0;
1495 if((errorcode=readobject(lock->objectnode, &cb, &o))==0) {
1496 newtransaction();
1498 errorcode=bumpobject(cb, o);
1499 checknotifyforobject(cb, o, TRUE);
1501 if(errorcode==0) {
1502 endtransaction();
1504 else {
1505 deletetransaction();
1509 /* Ignore any errorcodes -- not really interesting when closing a file... */
1512 if((errorcode=freelock(lock))!=0) {
1513 returnpacket(DOSFALSE,errorcode);
1515 else {
1516 returnpacket(DOSTRUE,0);
1519 break;
1520 case ACTION_DELETE_OBJECT:
1522 LONG errorcode;
1524 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1526 _DEBUG(("ACTION_DELETE_OBJECT(0x%08lx,'%s')\n",BADDR(globals->packet->dp_Arg1),globals->string));
1528 do {
1529 newtransaction();
1531 if((errorcode=deleteobject(BADDR(globals->packet->dp_Arg1), validatepath(globals->string), TRUE))==0) {
1532 endtransaction();
1534 else {
1535 deletetransaction();
1537 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1539 if(errorcode!=0) {
1540 returnpacket(DOSFALSE,errorcode);
1542 else {
1543 ULONG files,blocks;
1545 if((errorcode=getrecycledinfo(&files, &blocks))==0) {
1546 if(files>35) {
1547 cleanupdeletedfiles();
1551 returnpacket(DOSTRUE,0);
1554 break;
1555 case ACTION_RENAME_DISK:
1556 _DEBUG(("ACTION_RENAME_DISK\n"));
1559 struct CacheBuffer *cb;
1560 LONG errorcode;
1562 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
1563 if(AttemptLockDosList(LDF_WRITE|LDF_VOLUMES)!=0) {
1564 struct fsObjectContainer *oc=cb->data;
1565 UBYTE *s;
1566 UBYTE *d;
1567 #ifndef USE_FAST_BSTR
1568 UBYTE len;
1569 #endif
1571 /* Succesfully locked the doslist */
1573 newtransaction();
1575 preparecachebuffer(cb);
1578 s=BADDR(globals->packet->dp_Arg1);
1579 d=oc->object[0].name;
1580 d[copybstrasstr((BSTR)globals->packet->dp_Arg1, d, 30)+1] = 0;
1581 #if 0
1582 len=*s++;
1584 if(len>30) {
1585 len=30;
1588 while(len-->0) {
1589 *d++=*s++;
1591 *d++=0;
1592 *d=0; /* Zero for comment */
1593 #endif
1594 if((errorcode=storecachebuffer(cb))==0 && globals->volumenode!=0) {
1595 s=BADDR(globals->packet->dp_Arg1);
1596 d=BADDR(globals->volumenode->dl_Name);
1597 #ifdef USE_FAST_BSTR
1598 copystr(s, d, 30);
1599 #else
1600 len=*s++;
1602 if(len>30) {
1603 len=30;
1606 *d++=len;
1607 while(len-->0) {
1608 *d++=*s++;
1610 *d=0;
1611 #endif
1614 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
1616 if(errorcode==0) {
1617 endtransaction();
1619 else {
1620 deletetransaction();
1623 else {
1624 /* Doslist locking attempt was unsuccesful. Send this message back to our
1625 port to try it again later. */
1627 PutMsg(msgportpackets,msg);
1628 break;
1632 if(errorcode==0) {
1633 returnpacket(DOSTRUE,0);
1635 else {
1636 returnpacket(DOSFALSE,errorcode);
1639 break;
1640 case ACTION_RENAME_OBJECT:
1641 _DEBUG(("ACTION_RENAME_OBJECT\n"));
1644 LONG errorcode;
1646 do {
1647 struct CacheBuffer *cb;
1648 struct fsObject *o;
1649 struct ExtFileLock *lock;
1650 UBYTE *newname;
1651 UBYTE *s;
1653 lock=BADDR(globals->packet->dp_Arg1);
1654 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
1656 if((errorcode=locateobjectfromlock(lock,validatepath(globals->string),&cb,&o))==0) {
1657 copybstrasstr((BSTR)globals->packet->dp_Arg4,globals->string,258);
1659 settemporarylock(BE2L(o->be_objectnode));
1661 newname=validatepath(globals->string);
1662 s=FilePart(newname);
1664 if(*s!=0) {
1665 newtransaction();
1666 if((errorcode=renameobject(cb,o,BADDR(globals->packet->dp_Arg3),newname))==0) {
1667 endtransaction();
1669 else {
1670 deletetransaction();
1673 else {
1674 errorcode=ERROR_INVALID_COMPONENT_NAME;
1677 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1679 cleartemporarylock();
1681 if(errorcode!=0) {
1682 returnpacket(DOSFALSE,errorcode);
1684 else {
1685 returnpacket(DOSTRUE,0);
1688 break;
1689 case ACTION_SET_COMMENT:
1690 _DEBUG(("ACTION_SET_COMMENT\n"));
1692 LONG errorcode;
1694 do {
1695 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1696 copybstrasstr((BSTR)globals->packet->dp_Arg4,globals->string2,258);
1698 newtransaction();
1699 if((errorcode=setcomment(BADDR(globals->packet->dp_Arg2),validatepath(globals->string),globals->string2))!=0) {
1700 deletetransaction();
1702 else {
1703 endtransaction();
1705 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1707 if(errorcode!=0) {
1708 returnpacket(DOSFALSE,errorcode);
1710 else {
1711 returnpacket(DOSTRUE,0);
1714 break;
1715 case ACTION_SET_DATE:
1716 case ACTION_SET_PROTECT:
1717 case ACTION_SET_OWNER:
1718 case ACTION_SFS_SET_OBJECTBITS:
1719 _XDEBUG((DEBUG_OBJECTS, "ACTION_SET_DATE or ACTION_SET_PROTECT or ACTION_SET_OWNER\n"));
1722 LONG errorcode;
1724 do {
1725 struct CacheBuffer *cb;
1726 struct fsObject *o;
1727 struct ExtFileLock *lock;
1729 lock=BADDR(globals->packet->dp_Arg2);
1730 copybstrasstr((BSTR)globals->packet->dp_Arg3,globals->string,258);
1732 if((errorcode=locatelockableobject(lock,validatepath(globals->string),&cb,&o))==0) {
1733 NODE objectnode=BE2L(o->be_objectnode);
1735 if(objectnode!=ROOTNODE) {
1737 settemporarylock(objectnode);
1739 newtransaction();
1740 preparecachebuffer(cb);
1742 if(globals->packet->dp_Type==ACTION_SET_DATE) {
1743 checksum_writelong_be(cb->data, &o->be_datemodified, datestamptodate((struct DateStamp *)globals->packet->dp_Arg4));
1745 // o->datemodified=datestamptodate((struct DateStamp *)packet->dp_Arg4);
1747 else if(globals->packet->dp_Type==ACTION_SET_PROTECT) {
1748 checksum_writelong_be(cb->data, &o->be_protection, globals->packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE));
1750 // o->protection=packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
1752 else if(globals->packet->dp_Type==ACTION_SET_OWNER) {
1753 // ULONG *owner=(ULONG *)&o->owneruid;
1755 checksum_writelong_be(cb->data, (ULONG *)&o->be_owneruid, globals->packet->dp_Arg4);
1757 // *owner=packet->dp_Arg4;
1759 else {
1760 o->bits=(globals->packet->dp_Arg4 & (OTYPE_HIDDEN|OTYPE_UNDELETABLE)) | (o->bits & ~(OTYPE_HIDDEN|OTYPE_UNDELETABLE));
1761 setchecksum(cb); // new
1764 if((errorcode=storecachebuffer_nochecksum(cb))==0) {
1765 struct GlobalHandle *gh;
1767 checknotifyforobject(cb,o,TRUE);
1768 endtransaction();
1770 if(globals->packet->dp_Type==ACTION_SET_PROTECT && (gh=findglobalhandle(objectnode))!=0) {
1771 gh->protection=globals->packet->dp_Arg4^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
1774 else {
1775 deletetransaction();
1778 else {
1779 errorcode=ERROR_OBJECT_WRONG_TYPE;
1783 /* this 'loop' will only be left if the disk wasn't full, or
1784 cleanupdeletedfiles() couldn't free up any more space. */
1786 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1788 cleartemporarylock();
1790 if(errorcode!=0) {
1791 returnpacket(DOSFALSE,errorcode);
1793 else {
1794 returnpacket(DOSTRUE,0);
1797 break;
1798 case ACTION_SET_FILE_SIZE:
1799 _DEBUG(("ACTION_SET_FILE_SIZE(0x%08lx,0x%08lx,0x%08lx)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1802 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1803 LONG newfilesize;
1804 LONG errorcode;
1806 if(globals->packet->dp_Arg3==OFFSET_BEGINNING) {
1807 newfilesize=globals->packet->dp_Arg2;
1809 else if(globals->packet->dp_Arg3==OFFSET_END) {
1810 newfilesize=lock->gh->size+globals->packet->dp_Arg2;
1812 else if(globals->packet->dp_Arg3==OFFSET_CURRENT) {
1813 newfilesize=lock->offset+globals->packet->dp_Arg2;
1815 else {
1816 returnpacket(-1,ERROR_BAD_NUMBER);
1817 break;
1820 if(newfilesize>=0) {
1821 ULONG data=lock->gh->data;
1823 do {
1824 errorcode=setfilesize(lock,newfilesize);
1825 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
1827 if(errorcode!=0) {
1828 lock->gh->data=data;
1831 else {
1832 errorcode=ERROR_SEEK_ERROR;
1835 if(errorcode==0) {
1836 returnpacket(lock->gh->size,0);
1838 else {
1839 returnpacket(-1,errorcode);
1842 break;
1843 case ACTION_SEEK:
1844 _XDEBUG((DEBUG_SEEK,"ACTION_SEEK(0x%08lx,0x%08lx,0x%08lx)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1847 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1848 struct GlobalHandle *gh=lock->gh;
1849 ULONG oldpos=lock->offset;
1850 LONG newpos;
1851 LONG errorcode;
1853 if(globals->packet->dp_Arg3==OFFSET_BEGINNING) {
1854 newpos=globals->packet->dp_Arg2;
1856 else if(globals->packet->dp_Arg3==OFFSET_END) {
1857 newpos=gh->size+globals->packet->dp_Arg2;
1859 else if(globals->packet->dp_Arg3==OFFSET_CURRENT) {
1860 newpos=lock->offset+globals->packet->dp_Arg2;
1862 else {
1863 returnpacket(-1,ERROR_BAD_NUMBER);
1864 break;
1867 if(newpos>=0 && newpos<=gh->size) {
1868 if(newpos!=oldpos) {
1869 errorcode=seek(lock,newpos);
1871 else {
1872 errorcode=0;
1875 else {
1876 errorcode=ERROR_SEEK_ERROR;
1879 if(errorcode==0) {
1880 returnpacket(oldpos,0);
1882 else {
1883 returnpacket(-1,errorcode);
1886 break;
1887 case ACTION_READ:
1888 _XDEBUG((DEBUG_IO,"ACTION_READ(0x%08lx,0x%08lx,%ld)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1891 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1892 struct GlobalHandle *gh=lock->gh;
1893 UBYTE *buffer=(UBYTE *)globals->packet->dp_Arg2;
1894 ULONG bytesleft;
1895 UBYTE *startofbuf=buffer;
1896 LONG errorcode;
1898 bytesleft=globals->packet->dp_Arg3;
1899 if(lock->offset+bytesleft > gh->size) {
1900 bytesleft=gh->size-lock->offset;
1903 if((errorcode=seektocurrent(lock))==0) {
1904 if((gh->protection & FIBF_READ)!=0) {
1905 struct CacheBuffer *extent_cb;
1906 struct fsExtentBNode *ebn;
1908 while(bytesleft>0 && (errorcode=findextentbnode(lock->curextent, &extent_cb, &ebn))==0) {
1909 ULONG bytestoread;
1910 ULONG offsetinblock=lock->extentoffset & globals->mask_block;
1911 BLCK ebn_next=BE2L(ebn->be_next);
1912 UWORD ebn_blocks=BE2W(ebn->be_blocks);
1914 _XDEBUG((DEBUG_IO,"ACTION_READ: buffer = 0x%p bytesleft = %ld, offsetinblock = %ld, ExtentBNode = %ld, ebn->data = %ld, ebn->blocks = %ld\n",
1915 buffer, bytesleft, offsetinblock, lock->curextent, BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
1917 if(offsetinblock!=0 || bytesleft<globals->bytes_block) {
1919 // _XDEBUG((DEBUG_IO,"ACTION_READ: nextextentbnode = %ld, extentnodesize = %ld, en->blocks = %ld\n",nextextentbnode,extentnodesize,(ULONG)ebn->blocks));
1921 bytestoread=globals->bytes_block-offsetinblock;
1923 /* Check if there are more bytes left in the block then we want to read */
1924 if(bytestoread > bytesleft) {
1925 bytestoread=bytesleft;
1928 if((errorcode=readbytes(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, offsetinblock, bytestoread))!=0) {
1929 break;
1932 else {
1934 bytestoread=(((ULONG)ebn_blocks)<<globals->shifts_block) - lock->extentoffset;
1936 /* Check if there are more bytes left in the Extent then we want to read */
1937 if(bytestoread > bytesleft) {
1938 bytestoread=bytesleft & ~globals->mask_block;
1941 if((errorcode=read(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, bytestoread>>globals->shifts_block))!=0) {
1942 break;
1946 seekforward(lock, ebn_blocks, ebn_next, bytestoread);
1948 bytesleft-=bytestoread;
1949 buffer+=bytestoread;
1951 _XDEBUG((DEBUG_IO,"ACTION_READ: bytesleft = %ld, errorcode = %ld\n",bytesleft,errorcode));
1954 else {
1955 errorcode=ERROR_READ_PROTECTED;
1959 _XDEBUG((DEBUG_IO,"ACTION_READ: errorcode = %ld, buffer = %ld, startofbuf = %ld\n",errorcode,buffer,startofbuf));
1961 if(errorcode!=0) {
1962 returnpacket(-1,errorcode);
1964 else {
1965 returnpacket(buffer-startofbuf,0);
1968 break;
1969 case ACTION_WRITE:
1970 _XDEBUG((DEBUG_IO,"ACTION_WRITE(0x%08lx,0x%08lx,%ld)\n",globals->packet->dp_Arg1,globals->packet->dp_Arg2,globals->packet->dp_Arg3));
1973 struct ExtFileLock *lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
1974 ULONG bytestowrite=globals->packet->dp_Arg3;
1975 LONG errorcode=0;
1977 do {
1978 struct GlobalHandle *gh=lock->gh;
1980 /* Save some values in case of an error: */
1982 BLCK curextent=lock->curextent;
1983 ULONG extentoffset=lock->extentoffset;
1984 ULONG offset=lock->offset;
1985 ULONG size=gh->size;
1986 ULONG data=gh->data;
1988 if(bytestowrite!=0) {
1989 newtransaction();
1991 if((errorcode=writetofile(lock, (UBYTE *)globals->packet->dp_Arg2, bytestowrite))==0) {
1992 endtransaction();
1994 else {
1995 lock->curextent=curextent;
1996 lock->extentoffset=extentoffset;
1997 lock->offset=offset;
1998 gh->size=size;
1999 gh->data=data;
2001 deletetransaction();
2004 } while(errorcode==ERROR_DISK_FULL && freeupspace()!=FALSE);
2006 if(errorcode!=0) {
2007 _XDEBUG((DEBUG_IO,"ACTION_WRITE returns (-1, %ld)\n",errorcode));
2009 returnpacket(-1,errorcode);
2011 else {
2012 _XDEBUG((DEBUG_IO,"ACTION_WRITE returns (%ld, 0)\n",bytestowrite));
2014 lock->bits|=EFL_MODIFIED;
2015 returnpacket(bytestowrite,0);
2018 break;
2019 case ACTION_FREE_LOCK:
2020 _XDEBUG((DEBUG_LOCK,"ACTION_FREE_LOCK\n"));
2023 LONG errorcode;
2025 if((errorcode=freelock((struct ExtFileLock *)BADDR(globals->packet->dp_Arg1)))!=0) {
2026 returnpacket(DOSFALSE,errorcode);
2027 break;
2029 returnpacket(DOSTRUE,0);
2031 break;
2032 case ACTION_EXAMINE_ALL:
2033 _DEBUG(("ACTION_EXAMINE_ALL\n"));
2036 struct ExtFileLock *lock;
2037 struct ExAllData *ead;
2038 struct ExAllData *prevead=0;
2039 struct ExAllControl *eac;
2040 struct CacheBuffer *cb;
2041 struct fsObject *o;
2042 ULONG eadsize;
2043 ULONG stringsize;
2044 LONG spaceleft;
2045 LONG errorcode;
2047 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2048 ead=(struct ExAllData *)globals->packet->dp_Arg2;
2049 eac=(struct ExAllControl *)globals->packet->dp_Arg5;
2050 spaceleft=globals->packet->dp_Arg3;
2052 eac->eac_Entries=0;
2054 if(lock==0) {
2055 _DEBUG(("ACTION_EXAMINE_ALL: Zero lock was passed in...\n"));
2057 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2058 break;
2061 if(globals->packet->dp_Arg4>ED_OWNER) {
2062 returnpacket(DOSFALSE,ERROR_BAD_NUMBER);
2063 break;
2066 if(eac->eac_LastKey==0) {
2067 if((errorcode=readobject(lock->objectnode,&cb,&o))==0) {
2068 if((o->bits & OTYPE_DIR)!=0) {
2069 if(o->object.dir.be_firstdirblock!=0) {
2070 if((errorcode=readcachebuffercheck(&cb,BE2L(o->object.dir.be_firstdirblock),OBJECTCONTAINER_ID))==0) {
2071 struct fsObjectContainer *oc=cb->data;
2073 o=oc->object;
2074 eac->eac_LastKey=BE2L(o->be_objectnode);
2077 else {
2078 errorcode=ERROR_NO_MORE_ENTRIES;
2081 else {
2082 errorcode=ERROR_OBJECT_WRONG_TYPE;
2086 else {
2087 if((errorcode=readobject(eac->eac_LastKey,&cb,&o))==ERROR_IS_SOFT_LINK) {
2088 errorcode=0;
2092 while(errorcode==0) {
2093 WORD namelength=strlen(o->name);
2094 WORD keepentry;
2096 stringsize=0;
2097 eadsize=0;
2099 switch(globals->packet->dp_Arg4) {
2100 default:
2101 case ED_OWNER:
2102 eadsize += 4; /* ed_OwnedGID, ed_OwnedUID */
2103 case ED_COMMENT:
2104 stringsize+=strlen(o->name+namelength+1)+1;
2105 eadsize += sizeof(UBYTE *); /* ed_Comment */
2106 case ED_DATE:
2107 eadsize += 12; /* ed_Ticks, ed_Mins, ed_Days */
2108 case ED_PROTECTION:
2109 eadsize += 4; /* ed_Prot */
2110 case ED_SIZE:
2111 eadsize += 4; /* ed_Size */
2112 case ED_TYPE:
2113 eadsize += 4;
2114 case ED_NAME:
2115 stringsize += namelength+1;
2116 eadsize += sizeof(APTR) * 2; /* ed_Name, ed_Next */
2117 break;
2120 // _DEBUG(("ACTION_EXAMINE_ALL: eadsize = %ld, stringsize = %ld, spaceleft = %ld, packet->dp_Arg4 = %ld\n",eadsize,stringsize,spaceleft,packet->dp_Arg4));
2122 if(spaceleft<eadsize+stringsize) {
2123 break;
2126 switch(globals->packet->dp_Arg4) {
2127 default:
2128 case ED_OWNER:
2129 ead->ed_OwnerUID=BE2W(o->be_owneruid);
2130 ead->ed_OwnerGID=BE2W(o->be_ownergid);
2131 case ED_COMMENT:
2133 UBYTE *src=o->name+namelength+1;
2134 UBYTE *dest=(UBYTE *)ead+eadsize;
2136 ead->ed_Comment=dest;
2138 while(*src!=0) {
2139 *dest++=*src++;
2140 eadsize++;
2143 *dest=0;
2144 eadsize++;
2146 case ED_DATE:
2147 datetodatestamp(BE2L(o->be_datemodified),(struct DateStamp *)&ead->ed_Days);
2148 case ED_PROTECTION:
2149 ead->ed_Prot=BE2L(o->be_protection)^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
2150 case ED_SIZE:
2151 if((o->bits & OTYPE_DIR)==0) {
2152 ead->ed_Size=BE2L(o->object.file.be_size);
2154 else {
2155 ead->ed_Size=0;
2157 case ED_TYPE:
2158 _DEBUG(("examine ED_TYPE, o->bits=%x, o->objectnode=%d\n", o->bits, BE2L(o->be_objectnode)));
2159 if((o->bits & OTYPE_LINK)!=0) {
2160 ead->ed_Type=ST_SOFTLINK;
2162 if((o->bits & OTYPE_DIR)==0) {
2163 ead->ed_Type=ST_FILE;
2165 else if (o->be_objectnode == L2BE(ROOTNODE)) {
2166 ead->ed_Type = ST_ROOT;
2168 else {
2169 ead->ed_Type=ST_USERDIR;
2171 case ED_NAME:
2173 UBYTE *src=o->name;
2174 UBYTE *dest=(UBYTE *)ead+eadsize;
2176 ead->ed_Name=dest;
2178 while(*src!=0) {
2179 *dest++=*src++;
2180 eadsize++;
2183 *dest=0;
2184 eadsize++;
2186 // _DEBUG(("Stored entry %s\n",ead->ed_Name));
2190 if(eac->eac_MatchString!=0) {
2191 keepentry=MatchPatternNoCase(eac->eac_MatchString,ead->ed_Name);
2193 else {
2194 keepentry=DOSTRUE;
2197 if(keepentry!=DOSFALSE && eac->eac_MatchFunc!=0) {
2199 #ifdef __AROS__
2200 keepentry=CALLHOOKPKT(eac->eac_MatchFunc, ead, (APTR)globals->packet->dp_Arg4);
2201 #else
2202 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;
2203 keepentry=hookfunc(eac->eac_MatchFunc,ead,packet->dp_Arg4);
2204 #endif
2207 if(keepentry!=DOSFALSE && (o->bits & OTYPE_HIDDEN)==0) {
2208 ead->ed_Next=0;
2209 eadsize = (eadsize + sizeof(APTR) - 1) & ~(sizeof(APTR) - 1);
2210 if(prevead!=0) {
2211 prevead->ed_Next=ead;
2213 prevead=ead;
2214 ead=(struct ExAllData *)((UBYTE *)ead+eadsize);
2215 spaceleft-=eadsize;
2216 eac->eac_Entries++;
2220 struct fsObjectContainer *oc=cb->data;
2221 UBYTE *endadr;
2223 o=nextobject(o);
2225 endadr=(UBYTE *)oc+globals->bytes_block-sizeof(struct fsObject)-2;
2227 if((UBYTE *)o>=endadr || o->name[0]==0) {
2228 if(oc->be_next!=0) {
2229 if((errorcode=readcachebuffercheck(&cb,BE2L(oc->be_next),OBJECTCONTAINER_ID))==0) {
2230 struct fsObjectContainer *oc=cb->data;
2232 o=oc->object;
2233 eac->eac_LastKey=BE2L(o->be_objectnode);
2236 else {
2237 errorcode=ERROR_NO_MORE_ENTRIES;
2240 else {
2241 eac->eac_LastKey=BE2L(o->be_objectnode);
2246 if(errorcode!=0) {
2247 returnpacket(DOSFALSE,errorcode);
2249 else {
2250 returnpacket(DOSTRUE,0);
2253 break;
2254 case ACTION_EXAMINE_NEXT:
2255 // _DEBUG(("ACTION_EXAMINE_NEXT(0x%08lx,0x%08lx)\n",BADDR(packet->dp_Arg1),BADDR(packet->dp_Arg2)));
2257 /* An entry is added to a directory in the first dir block with
2258 enough space to hold the entry. If there is no space, then
2259 a new block is added at the START of the directory, and the
2260 new entry is added there.
2262 In the directory block with enough space, the entry is added
2263 at the end of the block. The order of directory entries there
2264 fore is like this (square parenthesis indicate blocks):
2266 [789] [456] [123]
2268 This means we should scan the directory in the exact opposite
2269 order to avoid a trap when an entry is overwritten during
2270 directory scanning (MODE_NEW_FILE removes old entry, and adds
2271 a new one). See below:
2273 1. [123]; scan is at 1 (next = 2); 1 is overwritten.
2275 2. [231]; scan is at 2 (next = 3); 2 is overwritten.
2277 3. [312]; scan is at 3 (next = 1); 3 is overwritten.
2279 4. [123]; same as 1 -> loop!
2281 By scanning the internals of a dir block backwards the problem
2282 is solved:
2284 1. [123]; scan is at 3 (next = 2); 3 is overwritten.
2286 2. [123]; scan is at 2 (next = 1); 2 is overwritten.
2288 3. [132]; scan is at 1 (next = next block); 1 is overwritten.
2290 4. new block is loaded -> no loop. */
2293 struct ExtFileLock *lock;
2294 struct CacheBuffer *cb;
2295 struct FileInfoBlock *fib=BADDR(globals->packet->dp_Arg2);
2296 LONG errorcode=0;
2298 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2300 if(lock==0) {
2301 _DEBUG(("ACTION_EXAMINE_NEXT: Zero lock was passed in...\n"));
2303 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2304 break;
2308 if(lock->ocblck==0 && lock->ocnode==0) {
2309 _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"));
2311 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2312 break;
2316 if(lock->currentnode!=0xFFFFFFFF && fib->fib_DiskKey!=lock->currentnode) {
2317 struct fsObject *o;
2319 /* It looks like the lock was reallocated! In this case we must rebuild
2320 the state information in the lock. lock->currentnode, lock->nextnode
2321 and lock->nextnodeblock. */
2323 if((errorcode=readobject(fib->fib_DiskKey, &cb, &o))==0) {
2324 struct fsObjectContainer *oc=cb->data;
2326 lock->currentnode=fib->fib_DiskKey;
2328 if((o=prevobject(o, oc))!=0) {
2329 lock->nextnodeblock=cb->blckno;
2330 lock->nextnode=BE2L(o->be_objectnode);
2332 else {
2333 lock->nextnodeblock=BE2L(oc->be_next);
2334 lock->nextnode=0xFFFFFFFF;
2339 if(lock->nextnodeblock==0) {
2340 errorcode=ERROR_NO_MORE_ENTRIES;
2343 /* The passed in lock describes a directory. EXAMINE_NEXT should return
2344 this directory's entries one at the time. The lock has state information
2345 which helps determine at which entry we currently are. */
2347 while(errorcode==0 && (errorcode=readcachebuffercheck(&cb, lock->nextnodeblock, OBJECTCONTAINER_ID))==0) {
2348 struct fsObjectContainer *oc=cb->data;
2349 struct fsObject *o=0;
2350 UBYTE bits;
2352 if(lock->nextnode!=0xFFFFFFFF) {
2353 /*** It is possible findobject returns 0... */
2354 o=findobject(oc, lock->nextnode);
2357 if(o==0) {
2358 o=lastobject(oc);
2361 bits=o->bits;
2362 fillfib(fib, o);
2363 lock->currentnode=BE2L(o->be_objectnode);
2365 /* prepare for another EXAMINE_NEXT */
2367 /* We need to check if there is another object in this ObjectContainer
2368 following the one we just returned. If there is then return its
2369 node. If there isn't then return the next ObjectContainer ptr and
2370 set ocnode to zero. */
2372 if((o=prevobject(o, oc))!=0) {
2373 /* There IS another object */
2374 lock->nextnode=BE2L(o->be_objectnode);
2376 else {
2377 lock->nextnodeblock=BE2L(oc->be_next);
2378 lock->nextnode=0xFFFFFFFF;
2381 if((bits & OTYPE_HIDDEN)==0) {
2382 break;
2384 else if(lock->nextnodeblock==0) {
2385 errorcode=ERROR_NO_MORE_ENTRIES;
2386 break;
2390 if(errorcode==0) {
2391 returnpacket(DOSTRUE,0);
2393 else {
2394 returnpacket(DOSFALSE,errorcode);
2397 break;
2398 #if 0
2399 /******** OLD EXAMINE_NEXT CODE! */
2400 case ACTION_EXAMINE_NEXT:
2401 // _DEBUG(("ACTION_EXAMINE_NEXT(0x%08lx,0x%08lx)\n",BADDR(packet->dp_Arg1),BADDR(packet->dp_Arg2)));
2403 /* An entry is added to a directory in the first dir block with
2404 enough space to hold the entry. If there is no space, then
2405 a new block is added at the START of the directory, and the
2406 new entry is added there.
2408 In the directory block with enough space, the entry is added
2409 at the end of the block. The order of directory entries there
2410 fore is like this (square parenthesis indicate blocks):
2412 [789] [456] [123]
2414 This means we should scan the directory in the exact opposite
2415 order to avoid a trap when an entry is overwritten during
2416 directory scanning (MODE_NEW_FILE removes old entry, and adds
2417 a new one). See below:
2419 1. [123]; scan is at 1 (next = 2); 1 is overwritten.
2421 2. [231]; scan is at 2 (next = 3); 2 is overwritten.
2423 3. [312]; scan is at 3 (next = 1); 3 is overwritten.
2425 4. [123]; same as 1 -> loop!
2427 By scanning the internals of a dir block backwards the problem
2428 is solved:
2430 1. [123]; scan is at 3 (next = 2); 3 is overwritten.
2432 2. [123]; scan is at 2 (next = 1); 2 is overwritten.
2434 3. [132]; scan is at 1 (next = next block); 1 is overwritten.
2436 4. new block is loaded -> no loop. */
2439 struct ExtFileLock *lock;
2440 struct CacheBuffer *cb;
2441 struct FileInfoBlock *fib=BADDR(packet->dp_Arg2);
2442 LONG errorcode=0;
2444 lock=(struct ExtFileLock *)BADDR(packet->dp_Arg1);
2446 if(lock==0) {
2447 _DEBUG(("ACTION_EXAMINE_NEXT: Zero lock was passed in...\n"));
2449 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2450 break;
2454 if(lock->ocblck==0 && lock->ocnode==0) {
2455 _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"));
2457 returnpacket(DOSFALSE,ERROR_OBJECT_WRONG_TYPE);
2458 break;
2462 if(lock->currentnode!=0xFFFFFFFF && fib->fib_DiskKey!=lock->currentnode) {
2463 struct fsObject *o;
2465 /* It looks like the lock was reallocated! In this case we must rebuild
2466 the state information in the lock. lock->currentnode, lock->nextnode
2467 and lock->nextnodeblock. */
2469 if((errorcode=readobject(fib->fib_DiskKey, &cb, &o))==0) {
2470 struct fsObjectContainer *oc=cb->data;
2472 lock->currentnode=fib->fib_DiskKey;
2474 o=nextobject(o);
2476 if(isobject(o,oc)!=FALSE) {
2477 lock->nextnodeblock=cb->blckno;
2478 lock->nextnode=o->objectnode;
2480 else {
2481 lock->nextnodeblock=oc->next;
2482 lock->nextnode=0xFFFFFFFF;
2487 if(lock->nextnodeblock==0) {
2488 errorcode=ERROR_NO_MORE_ENTRIES;
2491 /* The passed in lock describes a directory. EXAMINE_NEXT should return
2492 this directory's entries one at the time. The lock has state information
2493 which helps determine at which entry we currently are. */
2495 while(errorcode==0 && (errorcode=readcachebuffercheck(&cb,lock->nextnodeblock,OBJECTCONTAINER_ID))==0) {
2496 struct fsObjectContainer *oc=cb->data;
2497 struct fsObject *o=0;
2498 UBYTE bits;
2500 if(lock->nextnode!=0xFFFFFFFF) {
2501 /*** It is possible findobject returns 0... */
2502 o=findobject(oc,lock->nextnode);
2504 if(o==0) {
2505 o=oc->object;
2508 bits=o->bits;
2509 fillfib(fib,o);
2510 lock->currentnode=o->objectnode;
2512 /* prepare for another EXAMINE_NEXT */
2514 /* We need to check if there is another object in this ObjectContainer
2515 following the one we just returned. If there is then return its
2516 node. If there isn't then return the next ObjectContainer ptr and
2517 set ocnode to zero. */
2519 o=nextobject(o);
2521 if(isobject(o,oc)!=FALSE) {
2522 /* There IS another object */
2523 lock->nextnode=o->objectnode;
2525 else {
2526 lock->nextnodeblock=oc->next;
2527 lock->nextnode=0xFFFFFFFF;
2530 if((bits & OTYPE_HIDDEN)==0) {
2531 break;
2533 else if(lock->nextnodeblock==0) {
2534 errorcode=ERROR_NO_MORE_ENTRIES;
2535 break;
2539 if(errorcode==0) {
2540 returnpacket(DOSTRUE,0);
2542 else {
2543 returnpacket(DOSFALSE,errorcode);
2546 break;
2547 #endif
2549 case ACTION_EXAMINE_OBJECT:
2550 case ACTION_EXAMINE_FH:
2551 _DEBUG(("ACTION_EXAMINE_OBJECT\n"));
2554 struct ExtFileLock *lock;
2555 struct CacheBuffer *cb;
2556 struct fsObject *o;
2557 NODE objectnode;
2558 LONG errorcode;
2560 if(globals->packet->dp_Type==ACTION_EXAMINE_OBJECT) {
2561 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2563 else {
2564 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2567 if(lock==0) {
2568 objectnode=ROOTNODE;
2570 else {
2571 objectnode=lock->objectnode;
2574 errorcode=readobject(objectnode,&cb,&o);
2575 if(errorcode==0 || errorcode==ERROR_IS_SOFT_LINK) {
2576 fillfib((struct FileInfoBlock *)BADDR(globals->packet->dp_Arg2),o);
2578 /* prepare for EXAMINE_NEXT */
2579 if(lock!=0 && (o->bits & OTYPE_DIR)!=0) {
2580 lock->currentnode=0xFFFFFFFF;
2581 lock->nextnode=0xFFFFFFFF;
2582 lock->nextnodeblock=BE2L(o->object.dir.be_firstdirblock);
2585 returnpacket(DOSTRUE,0);
2587 else {
2588 returnpacket(DOSFALSE,errorcode);
2592 break;
2593 case ACTION_INFO:
2594 _DEBUG(("ACTION_INFO\n"));
2597 struct ExtFileLock *lock=BADDR(globals->packet->dp_Arg1);
2598 struct InfoData *id=BADDR(globals->packet->dp_Arg2);
2600 if(lock!=0 && globals->volumenode!=(struct DeviceList *)BADDR(lock->volume)) {
2601 _DEBUG(("ACTION_INFO: returning error\n"));
2603 returnpacket(DOSFALSE,ERROR_DEVICE_NOT_MOUNTED);
2604 break;
2607 fillinfodata(id);
2609 returnpacket(DOSTRUE,0);
2611 break;
2612 case ACTION_MORE_CACHE:
2613 _DEBUG(("ACTION_MORE_CACHE\n"));
2616 LONG errorcode;
2618 errorcode=addcachebuffers(globals->packet->dp_Arg1);
2619 _DEBUG(("ACTION_MORE_CACHE: dp_Arg1 = %ld, totalbuffers = %ld, errorcode = %ld\n",globals->packet->dp_Arg1,globals->totalbuffers,errorcode));
2621 if(errorcode==0) {
2622 // returnpacket(DOSTRUE,totalbuffers);
2624 returnpacket(globals->totalbuffers,0);
2626 else {
2627 returnpacket(DOSFALSE,errorcode);
2630 break;
2631 case ACTION_CHANGE_MODE:
2633 struct ExtFileLock *lock=0;
2634 LONG errorcode=0;
2636 if(globals->packet->dp_Arg1==CHANGE_FH) {
2637 lock=(struct ExtFileLock *)((struct FileHandle *)(BADDR(globals->packet->dp_Arg2)))->fh_Arg1;
2639 else if(globals->packet->dp_Arg1==CHANGE_LOCK) {
2640 lock=BADDR(globals->packet->dp_Arg2);
2643 if(lock!=0) {
2644 if(lock->access!=globals->packet->dp_Arg3) {
2645 if(lock->access!=EXCLUSIVE_LOCK) {
2646 /* Convert shared lock into an exclusive lock. We need to check
2647 if there are no locks besides this one, and that we aren't
2648 trying to get an exclusive lock on the root (which is never
2649 allowed). */
2651 if(lock->objectnode!=ROOTNODE) {
2652 NODE objectnode=lock->objectnode;
2654 lock->objectnode=0; /* this makes sure that our lock is not taken into account by lockable() */
2656 if(lockable(objectnode,EXCLUSIVE_LOCK)!=DOSFALSE) {
2657 /* Lockable says it is possible to lock it exclusively! */
2659 lock->access=EXCLUSIVE_LOCK;
2661 else {
2662 errorcode=ERROR_OBJECT_IN_USE;
2665 lock->objectnode=objectnode;
2667 else {
2668 errorcode=ERROR_OBJECT_IN_USE;
2671 else {
2672 /* Convert exclusive lock into a shared lock. Should always be
2673 possible. */
2675 lock->access=SHARED_LOCK;
2679 else {
2680 errorcode=ERROR_OBJECT_WRONG_TYPE;
2683 if(errorcode==0) {
2684 returnpacket(DOSTRUE,0);
2686 else {
2687 returnpacket(DOSFALSE,errorcode);
2690 break;
2691 case ACTION_PARENT:
2692 case ACTION_PARENT_FH:
2693 _DEBUG(("ACTION_PARENT\n"));
2696 LONG errorcode;
2697 struct ExtFileLock *lock;
2699 if(globals->packet->dp_Type==ACTION_PARENT) {
2700 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2702 else {
2703 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2706 if(lock==0 || lock->objectnode==ROOTNODE) {
2707 returnpacket(0,0);
2708 break;
2711 globals->string[0]='/';
2712 globals->string[1]=0;
2713 if((errorcode=lockobject(lock,globals->string,SHARED_LOCK,&lock))!=0) {
2714 returnpacket(0,errorcode);
2716 else {
2717 returnpacket((SIPTR)TOBADDR(lock),0);
2720 break;
2721 case ACTION_COPY_DIR:
2722 case ACTION_COPY_DIR_FH:
2723 _DEBUG(("ACTION_COPY_DIR\n"));
2726 LONG errorcode;
2727 struct ExtFileLock *lock;
2729 if(globals->packet->dp_Type==ACTION_COPY_DIR) {
2730 lock=(struct ExtFileLock *)BADDR(globals->packet->dp_Arg1);
2732 else {
2733 lock=(struct ExtFileLock *)globals->packet->dp_Arg1;
2736 _DEBUG(("ACTION_COPY_DIR: lock=%p\n", lock));
2738 if((errorcode=lockobject(lock,"",SHARED_LOCK,&lock))!=0) {
2739 _DEBUG(("ACTION_COPY_DIR: Failed to obtain lock!\n"));
2740 returnpacket(0,errorcode);
2742 else {
2743 returnpacket((SIPTR)TOBADDR(lock),0);
2746 break;
2747 case ACTION_LOCATE_OBJECT:
2749 struct ExtFileLock *lock;
2750 LONG errorcode;
2752 copybstrasstr((BSTR)globals->packet->dp_Arg2,globals->string,258);
2754 _XDEBUG((DEBUG_LOCK,"ACTION_LOCATE_OBJECT(0x%08lx,'%s',0x%08lx)\n",BADDR(globals->packet->dp_Arg1),globals->string,globals->packet->dp_Arg3));
2756 if((errorcode=lockobject((struct ExtFileLock *)BADDR(globals->packet->dp_Arg1),validatepath(globals->string),globals->packet->dp_Arg3,&lock))!=0) {
2757 returnpacket(0,errorcode);
2759 else {
2760 returnpacket((SIPTR)TOBADDR(lock),0);
2763 break;
2764 case ACTION_SFS_LOCATE_OBJECT:
2766 struct CacheBuffer *cb;
2767 struct fsObject *o;
2768 struct ExtFileLock *lock=0;
2769 LONG errorcode;
2771 if((errorcode=readobject(globals->packet->dp_Arg1, &cb, &o))==0) {
2772 errorcode=lockobject2(o, globals->packet->dp_Arg2, &lock);
2775 if(errorcode!=0) {
2776 returnpacket(0, errorcode);
2778 else {
2779 returnpacket((IPTR)lock, 0);
2782 break;
2783 case ACTION_ADD_NOTIFY:
2785 struct NotifyRequest *nr;
2787 nr=(struct NotifyRequest *)globals->packet->dp_Arg1;
2789 nr->nr_Next = (IPTR)globals->notifyrequests;
2790 nr->nr_Prev = 0;
2791 if (globals->notifyrequests)
2792 globals->notifyrequests->nr_Prev = (IPTR)nr;
2793 globals->notifyrequests = nr;
2795 _DEBUG(("ACTION_ADD_NOTIFY: Starting notification on %s (flags 0x%08lx)\n",nr->nr_FullName,nr->nr_Flags));
2797 if((nr->nr_Flags & NRF_NOTIFY_INITIAL)!=0) {
2798 notify(nr);
2801 returnpacket(DOSTRUE,0);
2803 break;
2804 case ACTION_REMOVE_NOTIFY:
2806 struct NotifyRequest *nr;
2808 nr=(struct NotifyRequest *)globals->packet->dp_Arg1;
2810 _DEBUG(("ACTION_REMOVE_NOTIFY: Removing notification of %s\n",nr->nr_FullName));
2812 if((nr->nr_Flags & NRF_SEND_MESSAGE) != 0) {
2813 /* Removing all outstanding messages form msgport */
2814 while(GetMsg(nr->nr_stuff.nr_Msg.nr_Port)!=0) {
2816 nr->nr_MsgCount=0;
2819 if(nr->nr_Prev)
2820 ((struct NotifyRequest *)nr->nr_Prev)->nr_Next = nr->nr_Next;
2821 else
2822 globals->notifyrequests = (struct NotifyRequest *)nr->nr_Next;
2824 if (nr->nr_Next)
2825 ((struct NotifyRequest *)nr->nr_Next)->nr_Prev = nr->nr_Prev;
2827 nr->nr_Next = 0;
2828 nr->nr_Prev = 0;
2830 returnpacket(DOSTRUE,0);
2832 break;
2833 case ACTION_FLUSH:
2835 LONG errorcode;
2837 if((errorcode=flushcaches())==0) {
2838 returnpacket(DOSTRUE, 0);
2840 else {
2841 returnpacket(DOSFALSE, errorcode);
2844 break;
2845 case ACTION_SFS_READ_BITMAP:
2847 LONG errorcode;
2849 Forbid();
2850 Permit();
2852 errorcode=extractspace((UBYTE *)globals->packet->dp_Arg1, globals->packet->dp_Arg2, globals->packet->dp_Arg3);
2854 returnpacket(errorcode==0 ? DOSTRUE : DOSFALSE, errorcode);
2856 break;
2857 case ACTION_SFS_DEFRAGMENT_INIT:
2859 globals->block_defragptr=2;
2861 returnpacket(DOSTRUE, 0);
2863 break;
2864 case ACTION_SFS_DEFRAGMENT_STEP:
2866 LONG errorcode;
2868 globals->defragmentsteps=(ULONG *)globals->packet->dp_Arg1;
2869 globals->defragmentlongs=globals->packet->dp_Arg2 - 2;
2871 if(globals->defragmentsteps!=0) {
2872 *globals->defragmentsteps=0;
2875 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk before defragmentation\nbecause of error %ld.", "Retry|Cancel", errorcode)==1) {
2878 if(errorcode==0) {
2880 newtransaction();
2882 if((errorcode=step())!=0) {
2883 deletetransaction();
2885 else {
2886 endtransaction();
2888 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk during defragmentation\nbecause of error %ld.", "Retry|Cancel", errorcode)==1) {
2893 if(errorcode==0) {
2894 struct DefragmentStep *ds=(struct DefragmentStep *)globals->packet->dp_Arg1;
2896 while(ds->id!=0) {
2897 if(ds->id==AROS_LONG2BE(MAKE_ID('M','O','V','E')) && ds->length==3) {
2898 updatelocksaftermove(ds->data[1], ds->data[2], ds->data[0]);
2900 ds=(struct DefragmentStep *)((ULONG *)ds + 2 + ds->length);
2903 returnpacket(DOSTRUE, 0);
2905 else {
2906 returnpacket(DOSFALSE, errorcode);
2909 break;
2910 default:
2911 _DEBUG(("ERROR_ACTION_NOT_KNOWN (packettype = %ld)\n",globals->packet->dp_Type));
2912 returnpacket(DOSFALSE,ERROR_ACTION_NOT_KNOWN);
2913 break;
2916 else if(globals->disktype==ID_NO_DISK_PRESENT) {
2917 dumppackets(globals->packet,ERROR_NO_DISK);
2919 else {
2920 dumppackets(globals->packet,ERROR_NOT_A_DOS_DISK);
2923 break;
2926 } while(msg!=0);
2932 static void fillfib(struct FileInfoBlock *fib,struct fsObject *o)
2934 UBYTE *src;
2935 UBYTE *dest;
2936 UBYTE length;
2938 if (o->be_objectnode==L2BE(ROOTNODE)) {
2939 fib->fib_DirEntryType=ST_ROOT;
2940 fib->fib_Size=BE2L(o->object.file.be_size);
2941 fib->fib_NumBlocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1) >> globals->shifts_block;
2942 } else if((o->bits & OTYPE_LINK)!=0) {
2943 fib->fib_DirEntryType=ST_SOFTLINK;
2944 // fib->fib_DirEntryType=ST_USERDIR; // For compatibility with Diavolo 3.4 -> screw it, DOpus fails...
2945 fib->fib_Size=BE2L(o->object.file.be_size);
2946 fib->fib_NumBlocks=0;
2948 else if((o->bits & OTYPE_DIR)==0) {
2949 fib->fib_DirEntryType=ST_FILE;
2950 fib->fib_Size=BE2L(o->object.file.be_size);
2951 fib->fib_NumBlocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1) >> globals->shifts_block;
2953 else {
2954 fib->fib_DirEntryType=ST_USERDIR;
2955 fib->fib_Size=0;
2956 fib->fib_NumBlocks=1;
2958 fib->fib_Protection=BE2L(o->be_protection)^(FIBF_READ|FIBF_WRITE|FIBF_EXECUTE|FIBF_DELETE);
2959 fib->fib_EntryType=fib->fib_DirEntryType;
2960 fib->fib_DiskKey=BE2L(o->be_objectnode);
2961 fib->fib_OwnerUID=BE2W(o->be_owneruid);
2962 fib->fib_OwnerGID=BE2W(o->be_ownergid);
2963 datetodatestamp(BE2L(o->be_datemodified),&fib->fib_Date);
2965 src=o->name;
2966 dest = fib->fib_FileName;
2967 dest++;
2968 length=0;
2970 while(*src!=0) {
2971 *dest++=*src++;
2972 length++;
2974 fib->fib_FileName[0]=length;
2976 src++; /* comment follows name, so just skip the null-byte seperating them */
2978 dest = fib->fib_Comment;
2979 dest++;
2980 length=0;
2982 while(*src!=0) {
2983 *dest++=*src++;
2984 length++;
2986 fib->fib_Comment[0]=length;
2991 static struct DosPacket *getpacket(struct Process *p) {
2992 struct MsgPort *port=&p->pr_MsgPort; /* get port of our process */
2993 struct Message *msg;
2995 if((msg=GetMsg(port))!=0) {
2996 return((struct DosPacket *)msg->mn_Node.ln_Name);
2998 else {
2999 return(0);
3005 static struct DosPacket *waitpacket(struct Process *p) {
3006 struct MsgPort *port=&p->pr_MsgPort; /* get port of our process */
3007 struct Message *msg;
3009 WaitPort(port);
3011 msg=GetMsg(port);
3013 return((struct DosPacket *)msg->mn_Node.ln_Name);
3018 static void returnpacket(SIPTR res1,LONG res2) {
3019 struct Message *msg;
3020 struct MsgPort *replyport;
3022 globals->packet->dp_Res1=res1; /* set return codes */
3023 globals->packet->dp_Res2=res2;
3025 replyport=globals->packet->dp_Port; /* Get ReplyPort */
3027 msg=globals->packet->dp_Link; /* Pointer to the Exec-Message of the packet */
3029 globals->packet->dp_Port=&globals->mytask->pr_MsgPort; /* Setting Packet-Port back */
3031 msg->mn_Node.ln_Name=(char *)globals->packet; /* Connect Message and Packet */
3032 msg->mn_Node.ln_Succ=NULL;
3033 msg->mn_Node.ln_Pred=NULL;
3035 PutMsg(replyport,msg); /* Send the Message */
3040 static void returnpacket2(struct DosPacket *packet, SIPTR res1, LONG res2)
3042 struct Message *msg;
3043 struct MsgPort *replyport;
3045 D(bug("[SFS] Replying, results are %ld/%ld\n", res1, res2));
3047 packet->dp_Res1=res1; /* set return codes */
3048 packet->dp_Res2=res2;
3050 replyport=packet->dp_Port; /* Get ReplyPort */
3052 msg=packet->dp_Link; /* Pointer to the Exec-Message of the packet */
3054 packet->dp_Port=&globals->mytask->pr_MsgPort; /* Setting Packet-Port back */
3056 msg->mn_Node.ln_Name=(char *)packet; /* Connect Message and Packet */
3057 msg->mn_Node.ln_Succ=NULL;
3058 msg->mn_Node.ln_Pred=NULL;
3060 PutMsg(replyport,msg); /* Send the Message */
3065 void starttimeout() {
3066 /* From the AbortIO AutoDocs:
3067 iORequest - pointer to an I/O request block (must have been used
3068 at least once. May be active or finished). */
3070 if(globals->pendingchanges==FALSE) {
3071 globals->inactivitytimer_ioreq->tr_time.tv_secs=globals->inactivity_timeout/2;
3072 globals->inactivitytimer_ioreq->tr_time.tv_micro=(globals->inactivity_timeout*500000)%1000000;
3073 globals->inactivitytimer_ioreq->tr_node.io_Command=TR_ADDREQUEST;
3075 SendIO(&globals->inactivitytimer_ioreq->tr_node);
3077 globals->pendingchanges=TRUE;
3078 globals->timerreset=FALSE;
3080 else {
3081 globals->timerreset=TRUE; /* Indicates that during the timeout there was another request. */
3084 if(globals->activitytimeractive==FALSE) {
3085 globals->activitytimer_ioreq->tr_time.tv_secs=globals->activity_timeout;
3086 globals->activitytimer_ioreq->tr_time.tv_micro=0;
3087 globals->activitytimer_ioreq->tr_node.io_Command=TR_ADDREQUEST;
3089 SendIO(&globals->activitytimer_ioreq->tr_node);
3091 globals->activitytimeractive=TRUE;
3097 void stoptimeout(void) {
3099 if(globals->pendingchanges!=FALSE) {
3100 AbortIO(&globals->inactivitytimer_ioreq->tr_node);
3101 WaitIO(&globals->inactivitytimer_ioreq->tr_node);
3103 globals->pendingchanges=FALSE;
3104 globals->timerreset=FALSE;
3107 // if(activitytimeractive!=FALSE) {
3108 // AbortIO(&activitytimer_ioreq->tr_node);
3109 // WaitIO(&activitytimer_ioreq->tr_node);
3111 // activitytimeractive=FALSE;
3112 // }
3117 LONG flushcaches() {
3118 LONG errorcode;
3120 /* Flushes any pending changes to disk and ask the user what to do when
3121 an error occurs. */
3123 while((errorcode=flushtransaction())!=0 && req("Pending buffers couldn't be flushed\nto the disk because of error %ld.", "Retry|Cancel", errorcode)==1) {
3126 motoroff();
3128 return(errorcode);
3133 void invalidatecaches() {
3135 /* Invalidates all caches without flushing. Call flushcaches()
3136 first. */
3138 invalidatecachebuffers();
3139 invalidateiocaches();
3143 void dreqArgs(UBYTE *fmt, APTR params)
3145 APTR args[4];
3146 UBYTE *fmt2;
3148 if(globals->debugreqs!=FALSE) {
3149 args[0]=AROS_BSTR_ADDR(globals->devnode->dn_Name);
3150 args[1]=AROS_BSTR_ADDR(globals->startupmsg->fssm_Device);
3151 args[2]=(APTR)globals->startupmsg->fssm_Unit;
3152 args[3]=fmt;
3154 if((fmt2=AllocVec(strlen(fmt)+100,0))!=0) {
3155 _DEBUG(("\nREQUESTER\n\n"));
3156 RawDoFmt("SmartFilesystem %s: (%s, unit %ld)\n\n%s",args,putChFunc,fmt2);
3158 if (requestArgs(PROGRAMNAME, fmt2, "Continue|No more requesters", params) == 0)
3159 globals->debugreqs=FALSE;
3161 FreeVec(fmt2);
3166 LONG reqArgs(UBYTE *fmt, UBYTE *gads, APTR params)
3168 APTR args[5];
3169 APTR *arg=args;
3170 UBYTE *fmt2;
3171 LONG gadget=0;
3173 /* Simple requester function. It will put up a requester
3174 in the form of:
3176 "Volume 'BOOT' (DH0: scsi.device, unit 0)"
3180 "Device DH0: (scsi.device, unit 0)"
3182 This depends on whether or not there is a valid
3183 VolumeNode. */
3185 if(globals->volumenode!=0) {
3186 *arg++=AROS_BSTR_ADDR(globals->volumenode->dl_Name);
3189 *arg++=AROS_BSTR_ADDR(globals->devnode->dn_Name);
3190 *arg++=AROS_BSTR_ADDR(globals->startupmsg->fssm_Device);
3191 *arg++=(APTR)globals->startupmsg->fssm_Unit;
3192 *arg=fmt;
3194 if((fmt2=AllocVec(strlen(fmt)+100,0))!=0) {
3196 if(globals->volumenode!=0) {
3197 RawDoFmt("Volume '%s' (%s: %s, unit %ld)\n\n%s",args,putChFunc,fmt2);
3199 else {
3200 RawDoFmt("Device %s: (%s, unit %ld)\n\n%s",args,putChFunc,fmt2);
3203 gadget = requestArgs(PROGRAMNAME " request", fmt2, gads, params);
3204 FreeVec(fmt2);
3207 return(gadget);
3210 LONG req_unusualArgs(UBYTE *fmt, APTR params)
3212 APTR args[5];
3213 UBYTE *fmt2;
3214 LONG gadget=0;
3216 /* Simple requester function. */
3217 args[0]=AROS_BSTR_ADDR(globals->devnode->dn_Name);
3218 args[1]=AROS_BSTR_ADDR(globals->startupmsg->fssm_Device);
3219 args[2]=(APTR)globals->startupmsg->fssm_Unit;
3220 args[3]=fmt;
3221 args[4]="This is a safety check requester, which should\n"\
3222 "never appear under normal conditions. Please\n"\
3223 "notify the author about the error above and if\n"\
3224 "possible under what circumstances it appeared.\n\n"\
3225 "BEWARE: SFS might crash if you click Continue.\n"\
3226 " Please save your work first!";
3228 if((fmt2=AllocVec(strlen(fmt)+400,0))!=0) {
3230 RawDoFmt("SmartFilesystem %s: (%s, unit %ld)\n\n%s\n\n%s",args,putChFunc,fmt2);
3231 gadget = requestArgs(PROGRAMNAME " request", fmt2, "Continue", params);
3232 FreeVec(fmt2);
3235 return(gadget);
3238 void request2(UBYTE *text) {
3239 request(PROGRAMNAME, text, "Ok", 0);
3242 LONG requestArgs(UBYTE *title, UBYTE *fmt, UBYTE *gads, APTR params)
3244 struct EasyStruct es;
3246 es.es_StructSize=sizeof(struct EasyStruct);
3247 es.es_Flags=0;
3248 es.es_Title=title;
3249 es.es_TextFormat=fmt;
3250 es.es_GadgetFormat=gads;
3252 return EasyRequestArgs(0, &es, 0, params);
3255 void outputcachebuffer(struct CacheBuffer *cb) {
3256 ULONG *a;
3257 UWORD n;
3259 _DEBUG(("CacheBuffer at address 0x%08lx of block %ld (Locked = %ld, Bits = 0x%02lx)\n",cb,cb->blckno,(LONG)cb->locked,(LONG)cb->bits));
3261 a=cb->data;
3263 for(n=0; n<(globals->bytes_block>>5); n++) {
3264 _DEBUG(("%08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]));
3265 a+=8;
3272 LONG readroots(void)
3274 struct CacheBuffer *cb1;
3275 struct CacheBuffer *cb2;
3276 struct fsRootBlock *rb1;
3277 struct fsRootBlock *rb2;
3278 WORD rb1okay=TRUE;
3279 WORD rb2okay=TRUE;
3280 UQUAD first, last;
3281 LONG errorcode;
3283 if((errorcode=readcachebuffer(&cb1,0))!=0) {
3284 return(errorcode);
3287 lockcachebuffer(cb1);
3289 if((errorcode=readcachebuffer(&cb2,globals->blocks_total-1))!=0) {
3290 unlockcachebuffer(cb1);
3291 return(errorcode);
3294 unlockcachebuffer(cb1);
3296 rb1=cb1->data;
3297 rb2=cb2->data;
3299 if(checkchecksum(cb1)==DOSFALSE || rb1->bheader.id!=L2BE(DOSTYPE_ID) || rb1->bheader.be_ownblock!=0) {
3300 _DEBUG(("cb1/rb1 not ok!\n"));
3301 rb1okay=FALSE;
3304 _DEBUG(("checkchecksum(cb1)=%d, rb1->bheader.id=%08x (wanted %08x), rb1->bheader.ownblock=%d\n",
3305 checkchecksum(cb1),BE2L(rb1->bheader.id), DOSTYPE_ID, BE2L(rb1->bheader.be_ownblock)
3308 if(checkchecksum(cb2)==DOSFALSE || rb2->bheader.id!=L2BE(DOSTYPE_ID) || BE2L(rb2->bheader.be_ownblock)!=BE2L(rb2->be_totalblocks)-1) {
3309 _DEBUG(("cb2/rb2 not ok!\n"));
3310 rb2okay=FALSE;
3313 _DEBUG(("checkchecksum(cb2)=%d, rb2->bheader.id=%08x, rb2->bheader.ownblock=%d, rb2->be_totalblocks =%d\n",
3314 checkchecksum(cb2),BE2L(rb2->bheader.id), BE2L(rb2->bheader.be_ownblock), BE2L(rb2->be_totalblocks)
3317 if(rb1okay!=FALSE && rb2okay!=FALSE) {
3318 /* Both root blocks look okay. */
3321 if(rb1->sequencenumber!=rb2->sequencenumber) {
3322 // Sequence numbers differ!
3326 /* Check sizes stored in rootblock */
3327 if ((rb1->be_blocksize != L2BE(globals->bytes_block)) || (rb1->be_totalblocks!=L2BE(globals->blocks_total)))
3329 _DEBUG(("bad size in rb1!\n"));
3330 return(ERROR_NOT_A_DOS_DISK);
3334 * Historically SFS rootblock holds absolute start and end positions on the disk in bytes.
3335 * They are used for validation and nothing else.
3336 * However, a situation is possible when for example someone takes an image of SFS partition
3337 * and then tries to mount it.
3338 * In order to make it working we compare lengths, not positions. If length is okay, the rootblock
3339 * is assumed to be okay.
3341 first = ((UQUAD)BE2L(rb1->be_firstbyteh) << 32) | BE2L(rb1->be_firstbyte);
3342 last = ((UQUAD)BE2L(rb1->be_lastbyteh) << 32) | BE2L(rb1->be_lastbyte);
3344 if (last - first != globals->byte_high - globals->byte_low)
3346 _DEBUG(("bad value in rb1!\n"));
3347 return ERROR_NOT_A_DOS_DISK;
3350 if(rb1->be_version!=BE2W(STRUCTURE_VERSION)) {
3351 /* Different version! */
3353 request(PROGRAMNAME " request","%s\n"\
3354 "is in a format unsupported by this version\n"\
3355 "of the filesystem. Please reformat the disk or\n"\
3356 "install the correct version of this filesystem.",
3357 "Ok",AROS_BSTR_ADDR(globals->devnode->dn_Name));
3359 return(ERROR_NOT_A_DOS_DISK);
3362 globals->block_bitmapbase=BE2L(rb1->be_bitmapbase);
3363 globals->block_adminspace=BE2L(rb1->be_adminspacecontainer);
3364 globals->block_root=BE2L(rb1->be_rootobjectcontainer);
3365 globals->block_extentbnoderoot=BE2L(rb1->be_extentbnoderoot);
3366 globals->block_objectnoderoot=BE2L(rb1->be_objectnoderoot);
3368 if((rb1->bits & ROOTBITS_CASESENSITIVE)!=0) {
3369 globals->is_casesensitive=TRUE;
3371 else {
3372 globals->is_casesensitive=FALSE;
3375 if((rb1->bits & ROOTBITS_RECYCLED)!=0) {
3376 globals->has_recycled=TRUE;
3378 else {
3379 globals->has_recycled=FALSE;
3382 else {
3383 errorcode=ERROR_NOT_A_DOS_DISK;
3386 return(errorcode);
3391 struct DeviceList *usevolumenode(UBYTE *name, ULONG creationdate) {
3392 struct DosList *dol;
3393 struct DeviceList *vn=0;
3395 /* This function locates the specified volumenode, and if found
3396 uses it for the current volume inserted. If the node is not
3397 found this function returns 0. If the specified volumenode
3398 is found, but is found to be in use, then this function
3399 returns -1. Otherwise the found volumenode is returned. */
3401 dol=LockDosList(LDF_READ|LDF_VOLUMES);
3403 while((dol=FindDosEntry(dol, name, LDF_VOLUMES))!=0) {
3404 if(datestamptodate(&dol->dol_misc.dol_volume.dol_VolumeDate)==creationdate) { // Do volumes have same creation date?
3405 Forbid();
3406 if(dol->dol_misc.dol_volume.dol_LockList!=0 || ((struct DeviceList *)dol)->dl_unused!=0) { // Is volume not in use?
3407 struct NotifyRequest *nr;
3408 struct ExtFileLock *lock;
3410 /* Volume is not currently in use, so grab locklist & notifyrequests and patch fl_Task fields */
3412 _DEBUG(("usevolumenode: Found DosEntry with same date, and locklist!=0\n"));
3414 lock=(struct ExtFileLock *)BADDR(dol->dol_misc.dol_volume.dol_LockList);
3415 nr=(struct NotifyRequest *)BADDR((((struct DeviceList *)dol)->dl_unused));
3416 dol->dol_misc.dol_volume.dol_LockList=0;
3417 ((struct DeviceList *)dol)->dl_unused=0;
3419 Permit();
3421 globals->locklist=lock;
3422 globals->notifyrequests=nr;
3424 while(lock!=0) {
3425 lock->task=&globals->mytask->pr_MsgPort;
3426 lock=lock->next;
3429 while(nr!=0) {
3430 nr->nr_Handler=&globals->mytask->pr_MsgPort;
3431 nr = (struct NotifyRequest *)nr->nr_Next;
3434 vn=(struct DeviceList *)dol;
3435 break;
3437 else {
3438 Permit();
3440 _DEBUG(("usevolumenode: Found DosEntry with same date, but it is in use!\n"));
3442 vn=(struct DeviceList *)-1;
3446 /* Volume nodes are of different date, continue search */
3447 dol=NextDosEntry(dol, LDF_VOLUMES);
3450 UnLockDosList(LDF_READ|LDF_VOLUMES);
3452 return(vn);
3457 LONG initdisk() {
3459 /* This routine is called whenever a disk has changed (via ACTION_INHIBIT or
3460 on startup). The routine scans the disk and initializes the necessary
3461 variables. Steps:
3463 - Check if the disk is a valid DOS disk
3464 If so, get name and datestamp of the disk
3465 If valid, look for volume node of the same name and datestamp
3466 If so, retrieve locklist from dol_LockList (fix fl_Task fields!)
3467 and use existing volume node.
3468 If not, a new volume node is created.
3469 If the disk carries the same attributes as another ACTIVE volume
3470 the handler ignores the newly inserted volume.
3471 If not valid, R/W error requester -> "NDOS"
3472 If not valid type is "NDOS"
3476 changegeometry(globals->dosenvec);
3478 if(globals->volumenode==0) {
3479 struct CacheBuffer *cb;
3480 struct fsObjectContainer *oc=0;
3481 ULONG newdisktype;
3482 LONG errorcode;
3484 globals->diskstate=writeprotection();
3486 if((errorcode=readroots())==0) {
3488 /* Root blocks are valid for this filesystem */
3490 _DEBUG(("Initdisk: Root blocks read\n"));
3492 #ifdef STARTDEBUG
3493 dreq("Root blocks are okay!");
3494 #endif
3496 if((errorcode=checkfortransaction())==0) {
3498 _DEBUG(("Initdisk: Checked for an old Transaction and applied it if it was present.\n"));
3500 if((errorcode=readcachebuffercheck(&cb,globals->block_root,OBJECTCONTAINER_ID))==0) {
3501 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)cb->data+globals->bytes_block-sizeof(struct fsRootInfo));
3502 ULONG blocksfree=0;
3504 lockcachebuffer(cb);
3505 oc=cb->data;
3507 _DEBUG(("Initdisk: '%s' was inserted\n",oc->object[0].name));
3509 /* ROOT block is valid for this filesystem */
3511 /* We should count the number of set bits in the bitmap now */
3514 struct CacheBuffer *cb;
3515 struct fsBitmap *b;
3516 BLCK bitmapblock=globals->block_bitmapbase;
3517 UWORD cnt=globals->blocks_bitmap;
3518 WORD n;
3520 while(cnt-->0 && (errorcode=readcachebuffercheck(&cb,bitmapblock,BITMAP_ID))==0) {
3521 b=cb->data;
3523 for(n=0; n<((globals->bytes_block-sizeof(struct fsBitmap))>>2); n++) {
3524 if(b->bitmap[n]!=0) {
3525 if(b->bitmap[n]==0xFFFFFFFF) {
3526 blocksfree+=32;
3528 else {
3529 blocksfree+=bfcnto(b->bitmap[n]);
3533 bitmapblock++;
3537 unlockcachebuffer(cb);
3539 _DEBUG(("Initdisk: Traversed bitmap, found %ld free blocks\n",blocksfree));
3541 if(errorcode==0 && BE2L(ri->be_freeblocks)!=blocksfree) {
3542 if(ri->be_freeblocks!=0) {
3543 dreq("The number of free blocks (%ld) is incorrect.\n"\
3544 "According to the bitmap it should be %ld.\n"\
3545 "The number of free blocks will now be updated.", BE2L(ri->be_freeblocks), blocksfree);
3548 newtransaction();
3550 preparecachebuffer(cb);
3552 ri->be_freeblocks=L2BE(blocksfree);
3554 if((errorcode=storecachebuffer(cb))==0) {
3555 endtransaction();
3557 else {
3558 deletetransaction();
3562 if(errorcode==0) {
3563 _DEBUG(("Initdisk: A valid DOS disk\n"));
3565 newdisktype = DOSTYPE_ID;
3567 #ifdef STARTDEBUG
3568 dreq("There is a valid SFS disk present!");
3569 #endif
3571 else {
3573 #ifdef STARTDEBUG
3574 dreq("SFS disk is invalid; bitmap error.");
3575 #endif
3577 newdisktype=ID_NOT_REALLY_DOS;
3578 errorcode=ERROR_NOT_A_DOS_DISK;
3581 else {
3583 #ifdef STARTDEBUG
3584 dreq("SFS disk is invalid; root objectcontainer error.");
3585 #endif
3587 newdisktype=ID_NOT_REALLY_DOS;
3588 errorcode=ERROR_NOT_A_DOS_DISK;
3591 else {
3593 #ifdef STARTDEBUG
3594 dreq("SFS disk is invalid; transaction error.");
3595 #endif
3597 newdisktype=ID_NOT_REALLY_DOS;
3598 errorcode=ERROR_NOT_A_DOS_DISK;
3601 else if(errorcode==INTERR_CHECKSUM_FAILURE || errorcode==ERROR_NOT_A_DOS_DISK) {
3602 newdisktype=ID_NOT_REALLY_DOS;
3603 errorcode=ERROR_NOT_A_DOS_DISK;
3605 #ifdef STARTDEBUG
3606 dreq("SFS disk is invalid; checksum failure.");
3607 #endif
3609 else if(errorcode==INTERR_BLOCK_WRONG_TYPE) {
3610 #ifdef STARTDEBUG
3611 dreq("SFS disk is invalid; wrong block type.");
3612 #endif
3614 newdisktype=ID_NOT_REALLY_DOS;
3615 errorcode=ERROR_NOT_A_DOS_DISK;
3617 else if(errorcode==TDERR_DiskChanged) {
3618 #ifdef STARTDEBUG
3619 dreq("SFS disk is invalid; disk was changed.");
3620 #endif
3622 newdisktype=ID_NO_DISK_PRESENT;
3623 errorcode=ERROR_NO_DISK;
3625 else {
3626 #ifdef STARTDEBUG
3627 dreq("SFS disk is invalid; unreadable disk.");
3628 #endif
3630 newdisktype=ID_UNREADABLE_DISK;
3631 errorcode=ERROR_NOT_A_DOS_DISK;
3634 if(errorcode==0) {
3635 struct DeviceList *vn;
3636 struct fsRootInfo *ri=(struct fsRootInfo *)((UBYTE *)oc+globals->bytes_block-sizeof(struct fsRootInfo));
3638 _DEBUG(("initdisk: Checking for an existing volume-node\n"));
3640 if((vn=usevolumenode(oc->object[0].name, BE2L(ri->be_datecreated)))!=(struct DeviceList *)-1) {
3641 if(vn==0) {
3642 /* VolumeNode was not found, so we need to create a new one. */
3644 _DEBUG(("initdisk: No volume-node found, creating new one instead.\n"));
3646 if((vn=(struct DeviceList *)MakeDosEntry(" ",DLT_VOLUME))!=0) {
3647 struct SFSMessage *sfsm;
3648 UBYTE *d2=(UBYTE *)BADDR(vn->dl_Name);
3649 #ifdef AROS_FAST_BSTR
3650 copystr(oc->object[0].name, d2, 30);
3651 #else
3652 UBYTE *d=d2+1;
3653 UBYTE *s=oc->object[0].name;
3654 UBYTE len=0;
3656 while(*s!=0 && len<30) {
3657 *d++=*s++;
3658 len++;
3661 *d=0;
3662 *d2=len;
3663 #endif
3665 datetodatestamp(BE2L(ri->be_datecreated), &vn->dl_VolumeDate);
3667 _DEBUG(("initdisk: Sending msg.\n"));
3669 if((sfsm=AllocVec(sizeof(struct SFSMessage), MEMF_CLEAR))!=0) {
3670 sfsm->command=SFSM_ADD_VOLUMENODE;
3671 sfsm->data=(IPTR)vn;
3672 sfsm->msg.mn_Length=sizeof(struct SFSMessage);
3674 PutMsg(globals->sdlhport, (struct Message *)sfsm);
3677 else {
3678 errorcode=ERROR_NO_FREE_STORE;
3682 _DEBUG(("initdisk: Using new or old volumenode.\n"));
3684 if(errorcode==0) { /* Reusing the found VolumeNode or using the new VolumeNode */
3685 vn->dl_Task=globals->devnode->dn_Task;
3686 vn->dl_DiskType=globals->dosenvec->de_DosType;
3687 globals->volumenode=vn;
3690 else { /* Volume is in use by another handler -- stay off */
3691 _DEBUG(("Initdisk: Found DosEntry with same date, and locklist==0\n"));
3692 newdisktype=ID_NO_DISK_PRESENT; /* Hmmm... EXTREMELY unlikely, but may explain the strange bug Laire had. */
3693 errorcode=ERROR_NO_DISK;
3694 // vn=0;
3695 globals->volumenode=0;
3699 if(errorcode==0) {
3700 diskchangenotify(IECLASS_DISKINSERTED);
3703 globals->disktype=newdisktype;
3705 return(errorcode);
3707 else {
3708 return(0);
3714 static struct DosList *attemptlockdoslist(LONG tries, LONG delay) {
3715 struct DosList *dol;
3716 struct DosPacket *dp;
3718 for(;;) {
3719 dol=AttemptLockDosList(LDF_WRITE|LDF_VOLUMES);
3721 if(((IPTR)dol & ~1)!=0) {
3722 return(dol);
3725 if(--tries<=0) {
3726 return(0);
3729 if((dp=getpacket(globals->mytask))!=0) {
3730 if(handlesimplepackets(dp)==0) {
3731 dumppackets(dp, ERROR_NOT_A_DOS_DISK);
3734 else {
3735 Delay(delay);
3741 void removevolumenode(struct DosList *dol, struct DosList *vn) {
3742 while((dol=NextDosEntry(dol, LDF_VOLUMES))!=0) {
3743 if(dol==vn) {
3744 RemDosEntry(dol);
3745 break;
3751 static void deinitdisk() {
3753 /* This function flushes all caches, and then invalidates them.
3754 If successful, this function then proceeds to either remove
3755 the volumenode, or transfer any outstanding locks/notifies to
3756 it. Finally it notifies the system of the disk removal. */
3758 _DEBUG(("deinitdisk: entry\n"));
3760 flushcaches();
3761 invalidatecaches();
3763 if(globals->volumenode!=0) {
3765 /* We first check if the VolumeNode needs to be removed; if not
3766 then we do not lock the DosList and modify some of its fields.
3767 If it must be removed, we first attempt to lock the DosList
3768 synchronously; whether this fails or not, we always send a
3769 removal message to the DosList subtask. */
3771 if(globals->locklist!=0 || globals->notifyrequests!=0) {
3773 /* There are locks or notifyrequests, so we cannot kill the volumenode. */
3775 /* We haven't got the DosList locked here, but I think it is fairly
3776 safe to modify these fields directly. */
3778 Forbid();
3780 globals->volumenode->dl_Task=0;
3781 globals->volumenode->dl_LockList=MKBADDR(globals->locklist);
3782 globals->volumenode->dl_unused=MKBADDR(globals->notifyrequests);
3784 Permit();
3786 globals->locklist=0;
3787 globals->notifyrequests=0;
3789 else {
3790 struct SFSMessage *sfsm;
3791 struct DosList *dol=attemptlockdoslist(5, 1);
3793 _DEBUG(("deinitdisk: dol = %ld\n", dol));
3795 if(dol!=0) { /* Is DosList locked? */
3796 removevolumenode(dol, (struct DosList *)globals->volumenode);
3797 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
3800 _DEBUG(("deinitdisk: sending msg\n"));
3802 /* Even if we succesfully locked the DosList, we still should notify
3803 the DosList task to remove the node as well (and to free it), just
3804 in case a VolumeNode Add was still pending. */
3806 if((sfsm=AllocVec(sizeof(struct SFSMessage), MEMF_CLEAR))!=0) {
3807 sfsm->command=SFSM_REMOVE_VOLUMENODE;
3808 sfsm->data=(IPTR)globals->volumenode;
3809 sfsm->msg.mn_Length=sizeof(struct SFSMessage);
3811 PutMsg(globals->sdlhport, (struct Message *)sfsm);
3815 _DEBUG(("deinitdisk: done\n"));
3817 globals->volumenode=0;
3819 diskchangenotify(IECLASS_DISKREMOVED);
3825 LONG handlesimplepackets(struct DosPacket *packet) {
3826 LONG type=packet->dp_Type; /* After returnpacket, packet->dp_Type is invalid! */
3828 switch(packet->dp_Type) {
3829 case ACTION_IS_FILESYSTEM:
3830 returnpacket2(packet, DOSTRUE,0);
3831 break;
3832 #ifndef __AROS__
3833 case ACTION_GET_DISK_FSSM:
3834 returnpacket2(packet, (LONG)startupmsg,0);
3835 break;
3836 case ACTION_FREE_DISK_FSSM:
3837 returnpacket2(packet, DOSTRUE,0);
3838 break;
3839 #endif
3840 case ACTION_DISK_INFO:
3841 actiondiskinfo(packet);
3842 break;
3843 case ACTION_SAME_LOCK:
3844 actionsamelock(packet);
3845 break;
3846 case ACTION_CURRENT_VOLUME:
3847 actioncurrentvolume(packet);
3848 break;
3849 default:
3850 return(0);
3853 return(type);
3858 static LONG dumppackets(struct DosPacket *packet,LONG returncode) {
3859 LONG type=packet->dp_Type; /* After returnpacket, packet->dp_Type is invalid! */
3861 /* Routine which returns ERROR_NOT_A_DOS_DISK for all known
3862 packets which cannot be handled under such a situation */
3864 switch(type) {
3865 case ACTION_READ:
3866 case ACTION_WRITE:
3867 case ACTION_SEEK:
3868 case ACTION_SET_FILE_SIZE:
3869 returnpacket2(packet, -1,returncode);
3870 break;
3871 case ACTION_CREATE_DIR:
3872 case ACTION_FINDUPDATE:
3873 case ACTION_FINDINPUT:
3874 case ACTION_FINDOUTPUT:
3875 case ACTION_END:
3876 case ACTION_EXAMINE_OBJECT:
3877 case ACTION_EXAMINE_NEXT:
3878 case ACTION_COPY_DIR:
3879 case ACTION_LOCATE_OBJECT:
3880 case ACTION_PARENT: /* till here verified to return 0 on error! */
3881 case ACTION_LOCK_RECORD:
3882 case ACTION_FREE_RECORD:
3883 case ACTION_FREE_LOCK:
3884 case ACTION_CHANGE_MODE:
3885 case ACTION_FH_FROM_LOCK:
3886 case ACTION_COPY_DIR_FH:
3887 case ACTION_PARENT_FH:
3888 case ACTION_EXAMINE_FH:
3889 case ACTION_EXAMINE_ALL:
3890 case ACTION_DELETE_OBJECT:
3891 case ACTION_RENAME_OBJECT:
3892 case ACTION_MAKE_LINK:
3893 case ACTION_READ_LINK:
3894 case ACTION_SET_COMMENT:
3895 case ACTION_SET_DATE:
3896 case ACTION_SET_PROTECT:
3897 case ACTION_INFO:
3898 case ACTION_RENAME_DISK:
3899 case ACTION_SERIALIZE_DISK:
3900 #ifndef __AROS__
3901 case ACTION_GET_DISK_FSSM:
3902 #endif
3903 case ACTION_MORE_CACHE:
3904 case ACTION_WRITE_PROTECT:
3905 case ACTION_ADD_NOTIFY:
3906 case ACTION_REMOVE_NOTIFY:
3907 case ACTION_INHIBIT: /* These packets are only dumped when doslist is locked */
3908 case ACTION_FORMAT:
3909 returnpacket2(packet, DOSFALSE,returncode);
3910 break;
3911 default:
3912 returnpacket2(packet, DOSFALSE,ERROR_ACTION_NOT_KNOWN);
3913 break;
3916 return(type);
3920 #ifdef DEBUGCODE
3921 static void dumppacket() {
3922 struct DosPacket *packet;
3924 /* routine which gets a packet and returns ERROR_NOT_A_DOS_DISK for all
3925 known packets which cannot be executed while we are attempting to access
3926 the doslist. Some packets which don't require disk access will be
3927 handled as normal. */
3929 packet=waitpacket(globals->mytask);
3931 if(handlesimplepackets(packet)==0) {
3932 dumppackets(packet,ERROR_NOT_A_DOS_DISK);
3935 #endif
3938 static void actioncurrentvolume(struct DosPacket *packet) {
3939 struct ExtFileLock *lock=(struct ExtFileLock *)packet->dp_Arg1;
3941 _DEBUG(("ACTION_CURRENT_VOLUME(%ld)\n",lock));
3943 if(lock==0) {
3944 _DEBUG(("ACTION_CURRENT_VOLUME: volumenode = %ld\n",globals->volumenode));
3945 returnpacket2(packet, (SIPTR)TOBADDR(globals->volumenode), 0);
3947 else {
3948 returnpacket2(packet, (SIPTR)lock->volume,0);
3954 static void actionsamelock(struct DosPacket *packet) {
3955 struct ExtFileLock *lock;
3956 struct ExtFileLock *lock2;
3958 lock=(struct ExtFileLock *)BADDR(packet->dp_Arg1);
3959 lock2=(struct ExtFileLock *)BADDR(packet->dp_Arg2);
3961 if(lock->objectnode==lock2->objectnode && lock->task==lock2->task) {
3962 returnpacket2(packet, DOSTRUE,0);
3963 return;
3965 returnpacket2(packet, DOSFALSE,0);
3970 static void actiondiskinfo(struct DosPacket *packet) {
3971 struct InfoData *id=BADDR(packet->dp_Arg1);
3973 _DEBUG(("ACTION_DISK_INFO\n"));
3975 fillinfodata(id);
3977 returnpacket2(packet, DOSTRUE,0);
3982 static void fillinfodata(struct InfoData *id) {
3983 ULONG usedblocks;
3985 id->id_NumSoftErrors=globals->numsofterrors;
3986 id->id_UnitNumber=globals->startupmsg->fssm_Unit;
3987 id->id_DiskState=globals->diskstate;
3988 id->id_NumBlocks=globals->blocks_total-globals->blocks_reserved_start-globals->blocks_reserved_end;
3990 usedblocks=id->id_NumBlocks;
3992 if(globals->disktype == DOSTYPE_ID) {
3993 ULONG deletedfiles, deletedblocks;
3995 getusedblocks(&usedblocks);
3996 if(getrecycledinfo(&deletedfiles, &deletedblocks)==0) {
3997 usedblocks-=deletedblocks;
4001 id->id_NumBlocksUsed=usedblocks;
4002 id->id_BytesPerBlock=globals->bytes_block;
4003 id->id_DiskType=globals->disktype;
4005 D(kprintf("Filling InfoData structure with a volumenode ptr to address %ld. Disktype = 0x%08lx\n", globals->volumenode, globals->disktype));
4007 id->id_VolumeNode=TOBADDR(globals->volumenode);
4009 _DEBUG(("fillinfodata: volumenode = %ld, disktype = 0x%08lx\n", globals->volumenode, globals->disktype));
4011 if(globals->locklist!=0) {
4012 id->id_InUse=DOSTRUE;
4014 else {
4015 id->id_InUse=DOSFALSE;
4021 BOOL checkchecksum(struct CacheBuffer *cb) {
4022 #ifdef CHECKCHECKSUMSALWAYS
4023 if(CALCCHECKSUM(globals->bytes_block,cb->data)==0) {
4024 #else
4025 if((cb->bits & CB_CHECKSUM)!=0 || CALCCHECKSUM(globals->bytes_block,cb->data)==0) { // -> copycachebuffer screws this up!
4026 #endif
4027 cb->bits|=CB_CHECKSUM;
4028 return(DOSTRUE);
4030 return(DOSFALSE);
4035 void setchecksum(struct CacheBuffer *cb) {
4036 struct fsBlockHeader *bh=cb->data;
4038 bh->be_checksum=0; /* Important! */
4039 bh->be_checksum=L2BE(-CALCCHECKSUM(globals->bytes_block,cb->data));
4044 LONG readcachebuffercheck(struct CacheBuffer **returnedcb,ULONG blckno,ULONG type) {
4045 LONG errorcode;
4046 struct fsBlockHeader *bh;
4048 while((errorcode=readcachebuffer(returnedcb,blckno))==0) {
4049 bh=(*returnedcb)->data;
4050 if(type!=0 && bh->id!=type) {
4051 dumpcachebuffers();
4052 outputcachebuffer(*returnedcb);
4053 emptycachebuffer(*returnedcb);
4055 if(blckno!=0) {
4056 if(request(PROGRAMNAME " request","%s\n"\
4057 "has a blockid error in block %ld.\n"\
4058 "Expected was blockid 0x%08lx,\n"\
4059 "but the block says it is blockid 0x%08lx.",
4060 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno,type,bh->id)<=0) {
4061 return(INTERR_BLOCK_WRONG_TYPE);
4064 else {
4065 /* if(request(PROGRAMNAME " request","%s\n"\
4066 "is not a DOS disk.",
4067 "Retry|Cancel",AROS_BSTR_ADDR(devnode->dn_Name),blckno,type,bh->id)<=0) {
4068 return(INTERR_BLOCK_WRONG_TYPE);
4069 } */
4071 return(INTERR_BLOCK_WRONG_TYPE);
4073 continue;
4075 if(checkchecksum(*returnedcb)==DOSFALSE) {
4076 dumpcachebuffers();
4077 outputcachebuffer(*returnedcb);
4078 emptycachebuffer(*returnedcb);
4080 if(request(PROGRAMNAME " request","%s\n"\
4081 "has a checksum error in block %ld.",
4082 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno)<=0) {
4083 return(INTERR_CHECKSUM_FAILURE);
4085 continue;
4087 if(BE2L(bh->be_ownblock)!=blckno) {
4088 dumpcachebuffers();
4089 outputcachebuffer(*returnedcb);
4090 emptycachebuffer(*returnedcb);
4092 if(request(PROGRAMNAME " request","%s\n"\
4093 "has a block error in block %ld.\n"\
4094 "Expected was block %ld,\n"\
4095 "but the block says it is block %ld.",
4096 "Reread|Cancel",AROS_BSTR_ADDR(globals->devnode->dn_Name),blckno,blckno,BE2L(bh->be_ownblock))<=0) {
4097 return(INTERR_OWNBLOCK_WRONG);
4099 continue;
4101 break;
4103 return(errorcode);
4108 /* How the CacheBuffers work:
4109 ==========================
4111 When the filesystem starts, the number of buffers indicated
4112 by the Mountlist are allocated. If the number of buffers
4113 is below the minimum then the filesystem will increase the
4114 number of buffers to the minimum. AddBuffers can be used
4115 later to change the number of CacheBuffers.
4117 Each CacheBuffer is linked into two chains. An LRU chain,
4118 to quickly determine the least recently used CacheBuffer,
4119 and a Hash chain. The Hash chain is used to quickly locate
4120 a CacheBuffer which contains a specific block. Empty
4121 cachebuffers are not linked in the Hash chain and have a
4122 blckno of zero.
4124 After having read a few blocks there comes a time when you
4125 want to modify them. The CacheBuffer systems helps to make
4126 it simple to do safe updates to blocks without ever running
4127 the risk of leaving the disk in a state where links or
4128 pointers are not yet valid. If you want to modify a block
4129 or series of blocks you need to tell this to the caching
4130 system by calling newtransaction(). An operation is a series
4131 of modifications which, when written to disk completely,
4132 would result in a valid disk. By calling newtransaction() you
4133 signal that you are about to start a series of modifications
4134 which together will again result in a valid disk.
4136 If however at any point during an operation somekind of
4137 error occurs which prevents you from finishing the operation
4138 you should call deletetransaction(). This will remove all
4139 changes you made from the point when you called
4140 newtransaction() from the cache. These blocks will now never
4141 get written to disk, so there is no chance that the disk
4142 becomes invalid by partial modifications.
4144 This sums up how the caching mechanism works. The internal
4145 workings of routines like newtransaction() and
4146 deletetransaction() will be explained later on. One very
4147 important routine hasn't been mentioned here:
4148 flushtransaction(). This is the routine which actually makes
4149 modifications to the disk. It does this by flushing one
4150 operation at a time to disk (in old to new order of course)
4151 in such a way that even if the machine crashed in the middle
4152 of the flush that the disk would remain valid. After
4153 flushing all operations the cache is cleaned up.
4160 /* Creating and moving objects
4162 1. Changing the comment on an object
4163 2. Changing the name of an object
4164 3. Creating a new object
4165 (4. Moving an object)
4167 Each of these three operations involves one or more of the following:
4169 1,2,3,4 - Locating enough new room for the object
4170 1,2,3,4 - Making sure the object-node points to the new block the
4171 object is located in.
4172 2 & 3 - Hashing the object
4173 3 - Allocating an ObjectNode.
4175 Order:
4177 'Allocating an ObjectNode' always precedes 'Hashing the Object' and 'Fixing the objectnode pointer'.
4178 'FindObjectSpace' always precedes 'Fixing the objectnode pointer'.
4180 1. FindObjectSpace OR Allocating an ObjectNode
4181 2. Fixing the objectnode pointer OR Hashing the Object */
4185 void fixlocks(struct GlobalHandle *gh,ULONG lastextentkey,ULONG lastextentoffset,ULONG filelength) {
4186 struct ExtFileLock *lock=globals->locklist;
4188 while(lock!=0) {
4189 if(lock->gh==gh) {
4190 if(filelength==0) {
4191 lock->offset=0;
4192 lock->extentoffset=0;
4193 lock->curextent=0;
4195 else if(lock->offset>filelength) {
4196 /* Hmmm, this lock is positioned beyond the EOF... */
4198 if(lock->offset-lock->extentoffset >= filelength) {
4199 lock->extentoffset=filelength-(lock->offset-lock->extentoffset);
4201 else {
4202 lock->extentoffset=filelength-lastextentoffset;
4203 lock->curextent=lastextentkey;
4205 lock->offset=filelength;
4208 lock=lock->next;
4214 static LONG extendblocksinfile(struct ExtFileLock *lock, ULONG blocks) {
4215 BLCK lastextentbnode;
4216 LONG errorcode;
4218 /* This function extends the file identified by the lock with a number
4219 of blocks. Only new Extents will be created -- the size of the file
4220 will not be altered, and changing it is left up to the caller. If
4221 the file did not have any blocks yet, then lock->curextent will be
4222 set to the first (new) ExtentBNode. It's left up up to the caller
4223 to reflect this change in object.file.data.
4225 This function must be called from within a transaction. */
4227 _XDEBUG((DEBUG_IO,"extendblocksinfile: Increasing number of blocks by %ld. lock->curextent = %ld\n",blocks,lock->curextent));
4229 if((errorcode=seektocurrent(lock))==0) {
4231 if((lastextentbnode=lock->curextent)!=0) {
4232 struct CacheBuffer *cb;
4233 struct fsExtentBNode *ebn;
4235 while((errorcode=findextentbnode(lastextentbnode,&cb,&ebn))==0 && BE2L(ebn->be_next)!=0) {
4236 lastextentbnode=BE2L(ebn->be_next);
4240 if(errorcode==0) {
4241 struct GlobalHandle *gh=lock->gh;
4243 /* We found the last Extent. Now we should see if we can extend it
4244 or if we need to add a new one and attach it to this one. */
4246 while(blocks!=0) {
4247 struct Space *sl=globals->spacelist;
4248 BLCK searchstart;
4250 if(lastextentbnode!=0) {
4251 struct CacheBuffer *cb;
4252 struct fsExtentBNode *ebn;
4254 if((errorcode=findextentbnode(lastextentbnode,&cb,&ebn))!=0) {
4255 break;
4257 searchstart=BE2L(ebn->be_key)+BE2W(ebn->be_blocks);
4259 else {
4260 searchstart=globals->block_rovingblockptr;
4263 if((errorcode=smartfindandmarkspace(searchstart,blocks))!=0) { /* only within transaction! */
4264 _DEBUG(("extendblocksinfile: sfams returned errorcode %ld\n", errorcode));
4265 break;
4268 _XDEBUG((DEBUG_IO,"extendblocksinfile: Found some space! blocks = %ld\n",blocks));
4270 while(blocks!=0 && sl->block!=0) {
4271 BLCK newspace=sl->block;
4272 ULONG newspaceblocks;
4274 newspaceblocks=sl->blocks > blocks ? blocks : sl->blocks;
4276 while(newspaceblocks!=0) {
4277 ULONG extentblocks;
4279 extentblocks=newspaceblocks > 8192 ? 8192 : newspaceblocks;
4281 newspaceblocks-=extentblocks;
4283 _XDEBUG((DEBUG_IO,"extendblocksinfile: sl->block = %ld, lastextentbnode = %ld, extentblocks = %ld\n",sl->block,lastextentbnode,extentblocks));
4285 if((errorcode=addblocks(extentblocks, newspace, gh->objectnode, &lastextentbnode))!=0) {
4286 _DEBUG(("extendblocksinfile: addblocks returned errorcode %ld\n", errorcode));
4287 return(errorcode);
4290 if(lock->curextent==0) {
4291 lock->curextent=lastextentbnode;
4294 lock->lastextendedblock=newspace + extentblocks;
4296 _XDEBUG((DEBUG_IO,"extendblocksinfile: Done adding or extending an extent -- blocks = %ld, extentblocks = %ld\n",blocks,extentblocks));
4298 blocks-=extentblocks;
4299 newspace+=extentblocks;
4301 sl++;
4307 return(errorcode);
4312 LONG truncateblocksinfile(struct ExtFileLock *lock,ULONG blocks,ULONG *lastextentkey,ULONG *lastextentoffset) {
4313 struct CacheBuffer *cb;
4314 struct fsExtentBNode *ebn;
4315 ULONG offset;
4316 ULONG extentoffset;
4317 LONG errorcode;
4319 /* Truncates the specified file to /blocks/ blocks. This function does not
4320 take care of setting the filesize. It also doesn't check for any locks
4321 which have a fileptr which is beyond the end of the file after calling
4322 this function. Its sole purpose is to reduce the number of blocks in
4323 the Extent list. */
4325 offset=blocks<<globals->shifts_block;
4327 _DEBUG(("truncateblocksinfile: truncating file by %ld blocks\n",blocks));
4329 if((errorcode=seekextent(lock,offset,&cb,&ebn,&extentoffset))==0) {
4331 _DEBUG(("truncateblocksinfile: offset = %ld, extentoffset = %ld\n",offset,extentoffset));
4333 if(offset-extentoffset==0) {
4334 ULONG prevkey=BE2L(ebn->be_prev);
4336 _DEBUG(("truncateblocksinfile: prevkey = %ld\n",prevkey));
4338 /* Darn. This Extent needs to be killed completely, meaning that we
4339 have to set the Next pointer of the previous Extent to zero. */
4341 deleteextents(BE2L(ebn->be_key));
4343 if((prevkey & 0x80000000)==0) {
4344 if((errorcode=findextentbnode(prevkey,&cb,&ebn))==0) {
4345 preparecachebuffer(cb);
4347 ebn->be_next=0;
4349 errorcode=storecachebuffer(cb);
4351 *lastextentkey=prevkey;
4352 *lastextentoffset=extentoffset-(BE2W(ebn->be_blocks)<<globals->shifts_block);
4355 else {
4356 struct fsObject *o;
4358 if((errorcode=readobject((prevkey & 0x7fffffff), &cb, &o))==0) {
4359 preparecachebuffer(cb);
4361 _DEBUG(("truncateblocksinfile: cb->blckno = %ld\n",cb->blckno));
4363 o->object.file.be_data=0;
4364 lock->gh->data=0;
4366 errorcode=storecachebuffer(cb);
4368 *lastextentkey=0;
4369 *lastextentoffset=0;
4373 else {
4374 ULONG newblocks=blocks-(extentoffset>>globals->shifts_block);
4376 *lastextentkey=BE2L(ebn->be_key);
4377 *lastextentoffset=extentoffset;
4379 _DEBUG(("truncateblocksinfile: newblocks = %ld, ebn->blocks = %ld\n",newblocks,BE2W(ebn->be_blocks)));
4381 if(newblocks!=BE2W(ebn->be_blocks)) {
4382 /* There is only one case where newblocks could equal en->blocks here,
4383 and that is if we tried to truncate the file to the same number of
4384 blocks the file already had! */
4386 lockcachebuffer(cb);
4388 if((errorcode=freespace(BE2L(ebn->be_key)+newblocks,BE2W(ebn->be_blocks)-newblocks))==0) {
4389 BLCK next=BE2L(ebn->be_next);
4391 preparecachebuffer(cb);
4393 ebn->be_blocks=W2BE(newblocks);
4394 ebn->be_next=0;
4396 if((errorcode=storecachebuffer(cb))==0) {
4397 errorcode=deleteextents(next); // Careful, deleting BNode's may change the location of other BNode's as well!
4401 unlockcachebuffer(cb);
4406 return(errorcode);
4411 LONG setfilesize(struct ExtFileLock *lock, ULONG bytes) {
4412 struct GlobalHandle *gh=lock->gh;
4413 LONG errorcode=0;
4415 /* This function sets the filesize of a file to /bytes/ bytes. If the
4416 file is not yet long enough, additional blocks are allocated and
4417 added to the file. If the file is too long the file will be shortened
4418 and the lost blocks are freed.
4420 Any filehandles using this file are set to the EOF if their current
4421 position would be beyond the new EOF when truncating the file.
4423 In any other case the position of the file ptr is not altered. */
4425 _DEBUG(("setfilesize: gh = 0x%08lx. Setting to %ld bytes. Current size is %ld bytes.\n",gh,bytes,gh->size));
4427 if(bytes!=gh->size) {
4428 LONG blocksdiff;
4429 LONG curblocks;
4431 curblocks=(gh->size+globals->bytes_block-1)>>globals->shifts_block;
4432 blocksdiff=((bytes+globals->bytes_block-1)>>globals->shifts_block) - curblocks;
4434 _DEBUG(("setfilesize: blocksdiff = %ld\n",blocksdiff));
4436 if(blocksdiff>0) {
4438 newtransaction();
4440 if((errorcode=extendblocksinfile(lock, blocksdiff))==0) {
4441 struct CacheBuffer *cb;
4442 struct fsObject *o;
4444 /* File (now) has right amount of blocks, only exact size in bytes may differ */
4446 if((errorcode=readobject(gh->objectnode, &cb, &o))==0) {
4447 preparecachebuffer(cb);
4449 o->object.file.be_size=L2BE(bytes);
4450 if(o->object.file.be_data==0) {
4451 o->object.file.be_data=L2BE(lock->curextent);
4454 errorcode=storecachebuffer(cb);
4458 if(errorcode==0) {
4459 endtransaction();
4460 gh->size=bytes;
4461 if(gh->data==0) {
4462 gh->data=lock->curextent;
4465 else {
4466 deletetransaction();
4469 else {
4470 ULONG lastextentkey=0;
4471 ULONG lastextentoffset=0;
4473 newtransaction();
4475 if(blocksdiff<0) {
4476 /* File needs to be shortened by -/blocksdiff/ blocks. */
4478 _DEBUG(("setfilesize: Decreasing number of blocks\n"));
4480 errorcode=truncateblocksinfile(lock,curblocks+blocksdiff,&lastextentkey,&lastextentoffset);
4483 _DEBUG(("setfilesize: lastextentkey = %ld, lastextentoffset = %ld\n",lastextentkey,lastextentoffset));
4485 if(errorcode==0) {
4486 struct CacheBuffer *cb;
4487 struct fsObject *o;
4489 /* File (now) has right amount of blocks, only exact size in bytes may differ */
4491 if((errorcode=readobject(gh->objectnode,&cb,&o))==0) {
4492 preparecachebuffer(cb);
4494 _DEBUG(("setfilesize: gh->objectnode = %ld, cb->blcno = %ld\n",gh->objectnode,cb->blckno));
4496 o->object.file.be_size=L2BE(bytes);
4498 errorcode=storecachebuffer(cb);
4502 if(errorcode==0) {
4503 endtransaction();
4504 gh->size=bytes;
4506 fixlocks(gh,lastextentkey,lastextentoffset,bytes);
4508 else {
4509 deletetransaction();
4513 if(errorcode==0) {
4514 lock->bits|=EFL_MODIFIED;
4518 return(errorcode);
4523 void seekforward(struct ExtFileLock *lock, UWORD ebn_blocks, BLCK ebn_next, ULONG bytestoseek) {
4525 /* This function does a simple forward seek. It assumes /bytestoseek/ is
4526 only large enough to skip within the current extent or to the very
4527 beginning of the next extent. */
4529 lock->offset+=bytestoseek;
4530 lock->extentoffset+=bytestoseek;
4531 if(lock->extentoffset >= ebn_blocks<<globals->shifts_block && ebn_next!=0) {
4532 lock->extentoffset=0;
4533 lock->curextent=ebn_next;
4539 LONG seektocurrent(struct ExtFileLock *lock) {
4540 LONG errorcode=0;
4542 /* This function checks if the currentextent is still valid. If not,
4543 it will attempt to locate the currentextent. */
4545 if(lock->curextent==0) {
4547 /* lock->curextent==0 can indicate 2 things:
4549 - A previously empty file was extended by another handle; in this
4550 case the file-ptr (lock->offset) of this handle is still zero.
4552 - The extent has been moved by the defragmenter, and will have to
4553 be re-located. In this case the file-ptr doesn't have to be
4554 zero. */
4556 if(lock->offset==0) {
4557 lock->curextent=lock->gh->data;
4559 else {
4560 struct CacheBuffer *cb;
4561 struct fsExtentBNode *ebn;
4562 ULONG extentoffset;
4564 if((errorcode=seekextent(lock, lock->offset, &cb, &ebn, &extentoffset))==0) {
4565 lock->curextent=BE2L(ebn->be_key);
4566 lock->extentoffset=lock->offset - extentoffset;
4571 return(errorcode);
4575 static LONG seek(struct ExtFileLock *lock,ULONG offset) {
4576 struct CacheBuffer *cb;
4577 struct fsExtentBNode *ebn;
4578 ULONG extentoffset;
4579 LONG errorcode;
4581 /* This function moves the file-ptr to the specified absolute file
4582 position. It will return an error if you try to seek beyond the
4583 end of the file. */
4585 _XDEBUG((DEBUG_SEEK,"seek: Attempting to seek to %ld\n",offset));
4587 if((errorcode=seekextent(lock,offset,&cb,&ebn,&extentoffset))==0) {
4588 lock->curextent=BE2L(ebn->be_key);
4589 lock->extentoffset=offset-extentoffset;
4590 lock->offset=offset;
4592 _XDEBUG((DEBUG_SEEK,"seek: lock->curextent = %ld, lock->extentoffset = %ld, lock->offset = %ld\n",lock->curextent,lock->extentoffset,lock->offset));
4595 _XDEBUG((DEBUG_SEEK,"seek: Exiting with errorcode %ld\n",errorcode));
4597 return(errorcode);
4602 LONG deleteextents(ULONG key) {
4603 struct CacheBuffer *cb;
4604 struct fsExtentBNode *ebn;
4605 LONG errorcode=0;
4607 /* Deletes an fsExtentBNode structure by key and any fsExtentBNodes linked to it.
4608 This function DOES NOT fix the next pointer in a possible fsExtentBNode which
4609 might have been pointing to the first BNode we are deleting. Make sure you check
4610 this yourself, if needed.
4612 If key is zero, than this function does nothing.
4614 newtransaction() should have been called prior to calling this function. */
4616 _XDEBUG((DEBUG_NODES,"deleteextents: Entry -- deleting extents from key %ld\n",key));
4619 while(key!=0 && (errorcode=findbnode(globals->block_extentbnoderoot,key,&cb,(struct BNode **)&ebn))==0) {
4620 /* node to be deleted located. */
4622 // _XDEBUG((DDEBUG_NODES,"deleteextents: Now deleting key %ld. Next key is %ld\n",key,ebn->next));
4624 key=BE2L(ebn->be_next);
4626 lockcachebuffer(cb); /* Makes sure freespace() doesn't reuse this cachebuffer */
4628 if((errorcode=freespace(BE2L(ebn->be_key), BE2W(ebn->be_blocks)))==0) {
4629 unlockcachebuffer(cb);
4631 // _XDEBUG((DDEBUG_NODES,"deleteextents: deletebnode from root %ld, with key %ld\n",block_extentbnoderoot,ebn->key));
4633 if((errorcode=deletebnode(globals->block_extentbnoderoot,BE2L(ebn->be_key)))!=0) { /*** Maybe use deleteinternalnode here??? */
4634 break;
4637 else {
4638 unlockcachebuffer(cb);
4639 break;
4643 // _XDEBUG((DEBUG_NODES,"deleteextents: Exiting with errorcode %ld\n",errorcode));
4645 return(errorcode);
4650 static LONG findextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode) {
4651 LONG errorcode;
4653 errorcode=findbnode(globals->block_extentbnoderoot,key,returned_cb,(struct BNode **)returned_bnode);
4655 #ifdef CHECKCODE_BNODES
4656 if(*returned_bnode==0 || BE2L((*returned_bnode)->be_key)!=key) {
4657 dreq("findextentbnode: findbnode() can't find key %ld!",key);
4658 outputcachebuffer(*returned_cb);
4659 return(INTERR_BTREE);
4661 #endif
4663 return(errorcode);
4668 LONG findobjectnode(NODE nodeno,struct CacheBuffer **returned_cb,struct fsObjectNode **returned_node) {
4669 return(findnode(block_objectnoderoot, sizeof(struct fsObjectNode), nodeno, returned_cb, (struct fsNode **)returned_node));
4674 static inline LONG createextentbnode(ULONG key,struct CacheBuffer **returned_cb,struct fsExtentBNode **returned_bnode) {
4675 return(createbnode(globals->block_extentbnoderoot,key,returned_cb,(struct BNode **)returned_bnode));
4680 LONG seekextent(struct ExtFileLock *lock, ULONG offset, struct CacheBuffer **returned_cb, struct fsExtentBNode **returned_ebn, ULONG *returned_extentoffset) {
4681 BLCK extentbnode;
4682 LONG errorcode=0;
4684 /* When seeking there are 2 options; we start from the current extent,
4685 or we start from the first extent. Below we determine the best
4686 startpoint: */
4688 if(offset>lock->gh->size) {
4689 _XDEBUG((DEBUG_SEEK,"seekextent: Attempting to seek beyond file\n"));
4691 return(ERROR_SEEK_ERROR);
4694 // _DEBUG(("seekextent: offset = %ld, lock->offset = %ld, lock->extentoffset = %ld, lock->curextent = %ld\n",offset, lock->offset, lock->extentoffset, lock->curextent));
4696 if(lock->curextent!=0 && offset >= lock->offset - lock->extentoffset) {
4697 extentbnode=lock->curextent;
4698 *returned_extentoffset=lock->offset - lock->extentoffset;
4700 else {
4701 extentbnode=lock->gh->data;
4702 *returned_extentoffset=0;
4705 /* Starting point has been determined. Let the seeking begin!
4706 We keep getting the next extent, until we find the extent which
4707 contains the required offset. */
4709 if(extentbnode!=0) {
4710 while((errorcode=findextentbnode(extentbnode,returned_cb,returned_ebn))==0) {
4711 ULONG endbyte=*returned_extentoffset+(BE2W((*returned_ebn)->be_blocks)<<globals->shifts_block);
4713 if(offset>=*returned_extentoffset && offset<endbyte) {
4714 /* Hooray! We found the correct extent. */
4715 break;
4718 if(BE2L((*returned_ebn)->be_next)==0) {
4719 /* This break is here in case we run into the end of the file.
4720 This prevents *returned_extentoffset and extentbnode
4721 from being destroyed in the lines below, since it is still
4722 valid to seek to the EOF */
4724 if(offset>endbyte) {
4725 /* There where no more blocks, but there should have been... */
4726 errorcode=ERROR_SEEK_ERROR;
4728 break;
4731 *returned_extentoffset+=BE2W((*returned_ebn)->be_blocks)<<globals->shifts_block;
4732 extentbnode=BE2L((*returned_ebn)->be_next);
4736 return(errorcode);
4741 /* Notify support functions. */
4743 UBYTE *fullpath(struct CacheBuffer *cbstart,struct fsObject *o) {
4744 struct fsObjectContainer *oc=cbstart->data;
4745 struct CacheBuffer *cb=cbstart;
4746 UBYTE *path=&globals->pathstring[519];
4747 UBYTE *name;
4749 /* Returns the full path of an object, or 0 if it fails. */
4751 lockcachebuffer(cbstart);
4753 *path=0;
4755 while(oc->be_parent!=0) { /* Checking parent here means name in ROOT will be ignored. */
4756 name=o->name;
4757 while(*++name!=0) {
4760 while(name!=o->name) {
4761 *--path=upperchar(*--name);
4764 if(readobject(BE2L(oc->be_parent),&cb,&o)!=0) {
4765 path=0;
4766 break;
4769 oc=cb->data;
4771 if(oc->be_parent!=0) {
4772 *--path='/';
4776 unlockcachebuffer(cbstart);
4778 return(path);
4783 void checknotifyforpath(UBYTE *path,UBYTE notifyparent) {
4784 struct NotifyRequest *nr;
4785 UBYTE *s1,*s2;
4786 UBYTE *lastslash;
4788 /* /path/ doesn't have a trailing slash and start with a root directory (no colon) */
4790 s1=path;
4791 lastslash=path-1;
4792 while(*s1!=0) {
4793 if(*s1=='/') {
4794 lastslash=s1;
4796 s1++;
4799 nr=globals->notifyrequests;
4801 // _DEBUG(("checknotify: path = '%s'\n", path));
4803 while(nr!=0) {
4805 s1=path;
4806 s2=stripcolon(nr->nr_FullName);
4808 while(*s1!=0 && *s2!=0 && *s1==upperchar(*s2)) {
4809 s1++; // If last character doesn't match, this increment won't take place.
4810 s2++;
4813 /* "" == "hallo" -> no match
4814 "" == "/" -> match
4815 "" == "" -> match
4816 "/shit" == "" -> match (parent dir)
4817 "shit" == "" -> match (parent dir) */
4819 // _DEBUG(("checknotify: fullpath = '%s', nr->FullName = '%s'\n",s1,s2));
4821 if( (s1[0]==0 && (s2[0]==0 || (s2[0]=='/' && s2[1]==0))) || ((s2[0]==0 && (s1==lastslash || s1==lastslash+1)) && notifyparent==TRUE) ) {
4822 /* Wow, the string in the NotifyRequest matches! We need to notify someone! */
4824 _DEBUG(("checknotify: Notificating!! nr->FullName = %s, UserData = 0x%08lx\n",nr->nr_FullName, nr->nr_UserData));
4826 notify(nr);
4828 /* No else, if neither flag is set then do nothing. */
4831 nr = (struct NotifyRequest *)nr->nr_Next;
4837 void checknotifyforobject(struct CacheBuffer *cb,struct fsObject *o,UBYTE notifyparent) {
4838 checknotifyforpath(fullpath(cb,o),notifyparent);
4843 void notify(struct NotifyRequest *nr) {
4845 /* This function sends a Notify to the client indicated by the passed
4846 in notifyrequest structure. */
4848 if((nr->nr_Flags & NRF_SEND_SIGNAL)!=0) {
4849 /* Sending them a signal. */
4851 _DEBUG(("notify: Sending signal\n"));
4853 Signal(nr->nr_stuff.nr_Signal.nr_Task,1<<nr->nr_stuff.nr_Signal.nr_SignalNum);
4855 else if((nr->nr_Flags & NRF_SEND_MESSAGE)!=0) {
4856 struct NotifyMessage *nm;
4858 /* Sending them a message. */
4860 _DEBUG(("notify: Sending message\n"));
4862 if((nm=AllocMem(sizeof(struct NotifyMessage),MEMF_CLEAR))!=0) {
4863 nm->nm_ExecMessage.mn_ReplyPort=globals->msgportnotify;
4864 nm->nm_ExecMessage.mn_Length=sizeof(struct NotifyMessage);
4866 nm->nm_Class=NOTIFY_CLASS;
4867 nm->nm_Code=NOTIFY_CODE;
4868 nm->nm_NReq=(struct NotifyRequest *)nr;
4870 _DEBUG(("notify: PutMsg() - UserData = 0x%08lx\n", nr->nr_UserData));
4872 PutMsg(nr->nr_stuff.nr_Msg.nr_Port,(struct Message *)nm);
4882 #if 0
4883 LONG writedata(ULONG newbytes, ULONG extentblocks, BLCK newspace, UBYTE *data) {
4884 ULONG blocks=newbytes>>shifts_block;
4885 LONG errorcode=0;
4887 if(blocks>extentblocks) {
4888 blocks=extentblocks;
4891 if(blocks!=0 && (errorcode=write(newspace,data,blocks))!=0) {
4892 return(errorcode);
4895 if(blocks<extentblocks) {
4896 struct CacheBuffer *cb;
4898 _XDEBUG((DEBUG_IO," writedata: blocks = %ld, newbytes = %ld\n",blocks,newbytes));
4900 newbytes-=blocks<<shifts_block;
4901 data+=blocks<<shifts_block;
4903 if((cb=newcachebuffer(newspace+blocks))!=0) {
4904 unlockcachebuffer(cb);
4906 CopyMem(data,cb->data,newbytes);
4908 errorcode=writecachebuffer(cb);
4909 /* At this point we can do 2 things with this cachebuffer. We can leave it
4910 hashed so if the user requests part of this block again it can quickly be grabbed
4911 from this CacheBuffer. The other option is the call emptycachebuffer()
4912 and let this buffer be reused ASAP. */
4914 emptycachebuffer(cb);
4916 else {
4917 errorcode=ERROR_NO_FREE_STORE;
4921 return(errorcode);
4923 #endif
4932 static LONG addblocks(UWORD blocks, BLCK newspace, NODE objectnode, BLCK *io_lastextentbnode) {
4933 struct CacheBuffer *cb;
4934 struct fsExtentBNode *ebn;
4935 LONG errorcode;
4937 /* This function adds /blocks/ blocks starting at block /newspace/ to a file
4938 identified by /objectnode/ and /lastextentbnode/. /io_lastextentbnode/ can
4939 be zero if there is no ExtentBNode chain attached to this file yet.
4941 This function must be called from within a transaction.
4943 /blocks/ ranges from 1 to 8192. To be able to extend Extents which are
4944 almost full, it is wise to make this value no higher than 8192 blocks.
4946 /io_lastextentbnode/ will contain the new lastextentbnode value when this
4947 function completes.
4949 This function makes no attempt to update any locks associated with this file,
4950 nor does it update the filesize information in a possible globalhandle.
4952 If there was no chain yet, then this function will create a new one. However
4953 it will NOT update object.file.data -- this is left up to the caller. */
4955 if(*io_lastextentbnode!=0) {
4956 /* There was already a ExtentBNode chain for this file. Extending it. */
4958 _XDEBUG((DEBUG_IO," addblocks: Extending existing ExtentBNode chain.\n"));
4960 if((errorcode=findextentbnode(*io_lastextentbnode,&cb,&ebn))==0) {
4962 preparecachebuffer(cb);
4964 if(BE2L(ebn->be_key)+BE2W(ebn->be_blocks)==newspace && BE2W(ebn->be_blocks)+blocks<65536) {
4965 /* It is possible to extent the last ExtentBNode! */
4967 _XDEBUG((DEBUG_IO," addblocks: Extending last ExtentBNode.\n"));
4969 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+blocks);
4971 errorcode=storecachebuffer(cb);
4973 else {
4974 /* It isn't possible to extent the last ExtentBNode so we create
4975 a new one and link it to the last ExtentBNode. */
4977 ebn->be_next=L2BE(newspace);
4979 if((errorcode=storecachebuffer(cb))==0 && (errorcode=createextentbnode(newspace,&cb,&ebn))==0) {
4981 _XDEBUG((DEBUG_IO," addblocks: Created new ExtentBNode.\n"));
4983 ebn->be_key=L2BE(newspace);
4984 ebn->be_prev=L2BE(*io_lastextentbnode);
4985 ebn->be_next=0;
4986 ebn->be_blocks=W2BE(blocks);
4988 *io_lastextentbnode=newspace;
4990 if((errorcode=storecachebuffer(cb))==0) {
4991 globals->block_rovingblockptr=newspace+blocks;
4992 if((blocks<<globals->shifts_block) <= ROVING_SMALL_WRITE) {
4993 globals->block_rovingblockptr+=ROVING_RESERVED_SPACE>>globals->shifts_block;
4996 if(globals->block_rovingblockptr>=globals->blocks_total) {
4997 globals->block_rovingblockptr=0;
5004 else {
5005 /* There is no ExtentBNode chain yet for this file. Attaching one! */
5007 if((errorcode=createextentbnode(newspace,&cb,&ebn))==0) {
5009 _XDEBUG((DEBUG_IO," addblocks: Created new ExtentBNode chain.\n"));
5011 ebn->be_key=L2BE(newspace);
5012 ebn->be_prev=L2BE(objectnode+0x80000000);
5013 ebn->be_next=0;
5014 ebn->be_blocks=W2BE(blocks);
5016 *io_lastextentbnode=newspace;
5018 if((errorcode=storecachebuffer(cb))==0) {
5019 globals->block_rovingblockptr=newspace+blocks;
5020 if((blocks<<globals->shifts_block) <= ROVING_SMALL_WRITE) {
5021 globals->block_rovingblockptr+=ROVING_RESERVED_SPACE>>globals->shifts_block;
5024 if(globals->block_rovingblockptr>=globals->blocks_total) {
5025 globals->block_rovingblockptr=0;
5031 return(errorcode);
5036 static void diskchangenotify(ULONG class) {
5037 struct IOStdReq *inputreq;
5038 struct MsgPort *inputport;
5039 struct InputEvent ie;
5040 struct timeval tv;
5042 if((inputport=CreateMsgPort())!=0) {
5043 if((inputreq=(struct IOStdReq *)CreateIORequest(inputport,sizeof(struct IOStdReq)))!=0) {
5044 if(OpenDevice("input.device",0,(struct IORequest *)inputreq,0)==0) {
5046 GetSysTime(&tv);
5048 ie.ie_NextEvent=0;
5049 ie.ie_Class=class;
5050 ie.ie_SubClass=0;
5051 ie.ie_Code=0;
5052 ie.ie_Qualifier=IEQUALIFIER_MULTIBROADCAST;
5053 ie.ie_EventAddress=0;
5054 ie.ie_TimeStamp=tv;
5056 inputreq->io_Command = IND_WRITEEVENT;
5057 inputreq->io_Length = sizeof(struct InputEvent);
5058 inputreq->io_Data = &ie;
5060 DoIO((struct IORequest *)inputreq);
5062 CloseDevice((struct IORequest *)inputreq);
5064 DeleteIORequest(inputreq);
5066 DeleteMsgPort(inputport);
5072 #if 0
5074 () {
5075 struct ExtFileLock *lock;
5077 lock=locklist;
5079 while(lock!=0) {
5080 if((lock->bits & EFL_MODIFIED)!=0) {
5081 /* File belonging to this lock was modified. */
5083 if(lock->gh!=0 && lock->curextent!=0) {
5084 BLCK lastblock=lock->curextent;
5086 lastblock+=lock->extentoffset>>shifts_block;
5088 /* lastblock is now possibly the last block which is used by the file. This
5089 should always be true for newly created files which are being extended. */
5095 lock=lock->next;
5099 #endif
5102 BOOL freeupspace(void) {
5103 BOOL spacefreed=FALSE;
5105 /* This function tries to free up space. It does this by
5106 permanently deleting deleted files (if case of a recycled)
5107 and by flushing the current transaction if there is one.
5109 If no space could be freed, then this function returns
5110 FALSE. This is the go-ahead sign to report disk full
5111 to the user. */
5113 if(hastransaction()) {
5114 flushtransaction();
5115 spacefreed=TRUE;
5118 if(cleanupdeletedfiles()!=FALSE) {
5119 spacefreed=TRUE;
5122 return(spacefreed);
5127 LONG writetofile(struct ExtFileLock *lock, UBYTE *buffer, ULONG bytestowrite) {
5128 struct GlobalHandle *gh=lock->gh;
5129 LONG errorcode;
5131 /* This function must be called from within a transaction! */
5133 if((gh->protection & FIBF_WRITE)!=0) {
5134 ULONG maxbytes;
5135 LONG newbytes;
5137 /* First thing we need to do is extend the file (if needed) to
5138 accomodate for all the data we are about to write. */
5140 maxbytes=(gh->size + globals->bytes_block-1) & ~globals->mask_block; // Maximum number of bytes file can hold with the current amount of blocks.
5141 newbytes=bytestowrite-(maxbytes-lock->offset); // Number of new bytes which would end up in newly allocated blocks.
5143 if((errorcode=seektocurrent(lock))==0 && (newbytes<=0 || (errorcode=extendblocksinfile(lock, (newbytes+globals->bytes_block-1)>>globals->shifts_block))==0)) {
5144 struct CacheBuffer *extent_cb;
5145 struct fsExtentBNode *ebn=0;
5146 ULONG newfilesize;
5148 /* At this point, the file either didn't need extending, or was succesfully extended. */
5150 newfilesize=lock->offset+bytestowrite;
5152 if(newfilesize<gh->size) {
5153 newfilesize=gh->size;
5156 /* If the filesize will change, then we set it below */
5158 if(newfilesize!=gh->size) {
5159 struct CacheBuffer *cb;
5160 struct fsObject *o;
5162 if((errorcode=readobject(lock->objectnode,&cb,&o))==0) {
5163 preparecachebuffer(cb);
5165 checksum_writelong_be(cb->data, &o->object.file.be_size, newfilesize);
5166 gh->size=newfilesize;
5168 if(o->object.file.be_data==0) {
5169 checksum_writelong_be(cb->data, &o->object.file.be_data, lock->curextent);
5170 gh->data=lock->curextent;
5173 errorcode=storecachebuffer_nochecksum(cb);
5177 if(errorcode==0) {
5178 while(bytestowrite!=0 && (errorcode=findextentbnode(lock->curextent, &extent_cb, &ebn))==0) {
5179 ULONG bytes;
5180 ULONG offsetinblock=lock->extentoffset & globals->mask_block;
5181 BLCK ebn_next=BE2L(ebn->be_next);
5182 UWORD ebn_blocks=BE2W(ebn->be_blocks);
5184 if(BE2W(ebn->be_blocks)==lock->extentoffset>>globals->shifts_block) {
5185 /* We are at the end +1 of this extent. Skip to next one. */
5187 lock->curextent=BE2L(ebn->be_next);
5188 lock->extentoffset=0;
5189 continue;
5192 if(offsetinblock!=0 || bytestowrite<globals->bytes_block) {
5194 /** Partial writes to the last block of the file will cause a
5195 read of that block, even if this block didn't yet contain
5196 any valid data, because the file was just extended. This
5197 is unneeded... */
5199 /* File-ptr is located somewhere in the middle of a block, or at the
5200 start of the block but not at the end of the file. To add data
5201 to it we'll first need to read this block. */
5203 _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));
5205 bytes=globals->bytes_block-offsetinblock;
5207 if(bytes>bytestowrite) {
5208 bytes=bytestowrite;
5211 // if(newbytes>0 && offsetinblock==0) { /** offsetinblock check is NOT redundant. */
5212 // struct CacheBuffer *cb=getcachebuffer();
5214 // CopyMem(buffer, cb->data, bytes);
5215 // errorcode=writethrough(lock->curextent + (lock->extentoffset>>shifts_block), cb->data, 1);
5216 // emptycachebuffer(cb);
5218 // if(errorcode!=0) {
5219 // break;
5220 // }
5221 // }
5222 // else {
5223 if((errorcode=writebytes(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, offsetinblock, bytes))!=0) {
5224 break;
5226 // }
5228 else {
5229 bytes=(((ULONG)ebn_blocks)<<globals->shifts_block) - lock->extentoffset;
5231 if(bytes > bytestowrite) {
5232 bytes=bytestowrite & ~globals->mask_block;
5234 /** This is a hack to speed up writes.
5236 What it does is write more bytes than there are in the buffer
5237 available (runaway writing), to avoid a seperate partial block
5238 write. It makes sure the extra data written doesn't extend
5239 into a new 4K page (assuming MMU is using 4K pages). */
5241 // #define MMU_PAGESIZE (524288)
5243 // if(((ULONG)(buffer+bytes) & ~(MMU_PAGESIZE-1))==((ULONG)(buffer+bytes+bytes_block-1) & ~(MMU_PAGESIZE-1))) {
5244 // bytes=bytestowrite;
5245 // }
5248 // _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));
5250 if((errorcode=write(BE2L(ebn->be_key)+(lock->extentoffset>>globals->shifts_block), buffer, (bytes+globals->bytes_block-1)>>globals->shifts_block))!=0) {
5251 break;
5255 seekforward(lock, ebn_blocks, ebn_next, bytes);
5257 bytestowrite-=bytes;
5258 buffer+=bytes;
5263 else {
5264 errorcode=ERROR_WRITE_PROTECTED;
5267 return(errorcode);
5272 LONG deletefileslowly(struct CacheBuffer *cbobject, struct fsObject *o) {
5273 ULONG size=BE2L(o->object.file.be_size);
5274 ULONG key=BE2L(o->object.file.be_data);
5275 LONG errorcode=0;
5277 /* cbobject & o refer to the file to be deleted (don't use this for objects
5278 other than files!). The file is deleted a piece at the time. This is
5279 because then even in low space situations files can be deleted.
5281 Note: This function deletes an object without first checking if
5282 this is allowed. Use deleteobject() instead. */
5284 _DEBUG(("deletefileslowly: Entry\n"));
5286 /* First we search for the last ExtentBNode */
5288 if(key!=0) {
5289 struct CacheBuffer *cb;
5290 struct fsExtentBNode *ebn;
5291 ULONG currentkey=0, prevkey=0;
5292 ULONG blocks=0;
5294 lockcachebuffer(cbobject);
5296 while((errorcode=findbnode(globals->block_extentbnoderoot,key,&cb,(struct BNode **)&ebn))==0) {
5297 if(BE2L(ebn->be_next)==0) {
5298 currentkey=BE2L(ebn->be_key);
5299 prevkey=BE2L(ebn->be_prev);
5300 blocks=BE2W(ebn->be_blocks);
5301 break;
5303 key=BE2L(ebn->be_next);
5306 /* Key could be zero (in theory) or contains the last ExtentBNode for this file. */
5308 if(errorcode==0) {
5309 while((key & 0x80000000)==0) {
5310 key=prevkey;
5312 newtransaction();
5314 lockcachebuffer(cb); /* Makes sure freespace() doesn't reuse this cachebuffer */
5316 if((errorcode=freespace(currentkey, blocks))==0) {
5317 unlockcachebuffer(cb);
5319 if((errorcode=deletebnode(globals->block_extentbnoderoot, currentkey))==0) {
5320 if((key & 0x80000000)==0) {
5321 if((errorcode=findbnode(globals->block_extentbnoderoot, key, &cb, (struct BNode **)&ebn))==0) {
5322 preparecachebuffer(cb);
5324 ebn->be_next=0;
5325 currentkey=BE2L(ebn->be_key);
5326 prevkey=BE2L(ebn->be_prev);
5327 blocks=BE2W(ebn->be_blocks);
5329 errorcode=storecachebuffer(cb);
5332 else {
5333 preparecachebuffer(cbobject);
5335 checksum_writelong_be(cbobject->data, &o->object.file.be_size, 0);
5336 checksum_writelong_be(cbobject->data, &o->object.file.be_data, 0);
5338 // o->object.file.data=0;
5339 // o->object.file.size=0;
5341 if((errorcode=storecachebuffer_nochecksum(cbobject))==0) {
5342 errorcode=setrecycledinfodiff(0, -((size+globals->bytes_block-1)>>globals->shifts_block));
5347 else {
5348 unlockcachebuffer(cb);
5351 if(errorcode==0) {
5352 /* Unlocked CacheBuffers could be killed after an endtransaction()! */
5353 endtransaction();
5355 else {
5356 deletetransaction();
5357 break;
5362 unlockcachebuffer(cbobject);
5365 _DEBUG(("deletefileslowly: Alternative way errorcode = %ld\n",errorcode));
5367 if(errorcode==0) {
5369 /* File-data was succesfully freed. Now remove the empty object. */
5371 newtransaction();
5373 if((errorcode=removeobject(cbobject, o))==0) {
5374 endtransaction();
5375 return(0);
5378 deletetransaction();
5381 _DEBUG(("deletefileslowly: Exiting with errorcode = %ld\n",errorcode));
5383 return(errorcode);
5389 Transactions
5390 ------------
5392 ReadCacheBuffer
5393 ReadOriginalCacheBuffer
5394 LockCacheBuffer
5395 UnLockCacheBuffer
5396 NewTransaction
5397 DeleteTransaction
5398 EndTransaction
5399 StoreCacheBuffer
5400 PrepareCacheBuffer
5402 The transaction system allows the filesystem to keep track
5403 of changes made to blocks in the cache. Every change is
5404 stored in a transaction buffer. The transaction buffer and
5405 the original version of a block can be used to restore the
5406 latest version of a block at any time.
5408 At any time you can use readcachebuffer() to get the latest
5409 version of a block. This could be a partially modified
5410 version if you're in the middle of a transaction, the latest
5411 version stored in the transaction buffer or the original
5412 version from disk.
5414 When you want to make changes to a block you call
5415 preparecachebuffer(). This checks if the block you're
5416 preparing to change is the original version, and if so makes
5417 a backup copy. This copy is not strictly needed, but it
5418 speeds up the filesystem to keep the original version around
5419 in case we need it later. The preparecachebuffer() function
5420 also calls lockcachebuffer() so the block you're changing
5421 won't be reused in subsequent cache operations.
5423 When you're satisfied with the changes you've made to a
5424 specific block you should call storecachebuffer(). This
5425 stores the changes into the transaction buffer. From that
5426 point on readcachebuffer() will be able to recreate the
5427 block with your new modifications.
5429 Before calling preparecachebuffer() for the first time, you
5430 need to call newtransaction(). This is to let the
5431 transaction system know you're about to start a new series
5432 of modifications.
5434 When you're done making all the changes to multiple blocks
5435 you need to call endtransaction(). This makes all the
5436 changes permanent. If there was an error along the way you
5437 can call deletetransaction() and all the changes you stored
5438 with storecachebuffer() since you're last call to
5439 newtransaction() will automatically be discarded.
5441 Any cachebuffers you had locked at the time which had
5442 changes in them will be restored to their original state (in
5443 reality these buffers will simply be reread using
5444 readcachebuffer()).
5446 Examples:
5448 x=readcachebuffer(0); // x.data = 1
5450 newtransaction();
5452 preparecachebuffer(x);
5454 x.data=2;
5456 storecachebuffer(cb);
5458 deletetransaction(); // x.data = 1;
5460 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); endtr() -> "CC";
5462 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); deltr() -> "AA";
5464 read(x) -> "AA"; newtr(); prep(x); write("CC") -> "CC"; store(x); newtr(); prep(x); write("EE") -> "EE"; store(x); deltr() -> "CC"; deltr() -> "AA";
5472 LONG setnextextent(BLCK next, BLCK key) {
5473 struct CacheBuffer *cb;
5474 struct fsExtentBNode *ebn;
5475 LONG errorcode=0;
5477 /* This function set the previous value of the
5478 passed Extent. If the passed Extent is zero
5479 then this function does nothing. */
5481 if(next!=0 && (errorcode=findextentbnode(next, &cb, &ebn))==0) {
5482 preparecachebuffer(cb);
5484 ebn->be_prev=L2BE(key);
5486 errorcode=storecachebuffer(cb);
5489 return(errorcode);
5494 LONG setprevextent(BLCK prev, BLCK key) {
5495 struct CacheBuffer *cb;
5496 struct fsExtentBNode *ebn;
5497 LONG errorcode;
5499 /* This function sets the next value of the
5500 passed Extent/Object to key. */
5502 if((prev & 0x80000000)==0 && (errorcode=findextentbnode(prev, &cb, &ebn))==0) {
5503 preparecachebuffer(cb);
5505 ebn->be_next=L2BE(key);
5507 errorcode=storecachebuffer(cb);
5509 else {
5510 struct fsObject *o;
5512 if((errorcode=readobject((prev & 0x7fffffff), &cb, &o))==0) {
5513 preparecachebuffer(cb);
5515 o->object.file.be_data=L2BE(key);
5517 errorcode=storecachebuffer(cb);
5521 return(errorcode);
5526 LONG mergeextent(BLCK key) {
5527 struct CacheBuffer *cb;
5528 struct fsExtentBNode *ebn;
5529 LONG errorcode;
5531 /* This function tries to merge the current extent with the
5532 next extent. If the extent can't be merged, or was
5533 succesfully merged then this function returns 0. Any
5534 error which occured during merging will be returned. */
5536 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5538 /* We check if we found the right key, if there is a next Extent and
5539 if the two Extents touch each other: */
5541 if(BE2L(ebn->be_key)==key && BE2L(ebn->be_next)!=0 && BE2L(ebn->be_key)+BE2W(ebn->be_blocks) == BE2L(ebn->be_next)) {
5542 struct CacheBuffer *cb2;
5543 struct fsExtentBNode *ebn2;
5545 if((errorcode=findextentbnode(BE2L(ebn->be_next), &cb2, &ebn2))==0 && BE2W(ebn2->be_blocks)+BE2W(ebn->be_blocks) < 65536) {
5546 BLCK next=BE2L(ebn2->be_next);
5548 /* Merge next extent with our extent */
5550 preparecachebuffer(cb);
5552 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+BE2W(ebn2->be_blocks));
5553 ebn->be_next=L2BE(next);
5555 if((errorcode=storecachebuffer(cb))==0) { // call storecachebuffer() here, because deletebnode() may move our BNode.
5557 if((errorcode=deletebnode(globals->block_extentbnoderoot, BE2L(ebn2->be_key)))==0) { /*** Maybe use deleteinternalnode here??? */
5558 errorcode=setnextextent(next, key);
5565 return(errorcode);
5570 LONG insertextent(BLCK key, BLCK next, BLCK prev, ULONG blocks) {
5571 struct CacheBuffer *cb=0;
5572 struct fsExtentBNode *ebn=0;
5573 LONG errorcode;
5575 /* This function creates a new extent, but won't create one if it can
5576 achieve the same effect by extending the next or previous extent.
5577 In the unlikely case that the new extent is located EXACTLY between
5578 its predecessor and successor then an attempt will be made to merge
5579 these 3 extents into 1. */
5582 prev available & mergeable -> merge.
5583 prev available, but not mergeable -> create new -> update next -> update previous.
5584 prev not available -> create new -> update next -> update object
5587 if((prev & 0x80000000)!=0 || (errorcode=findextentbnode(prev, &cb, &ebn))==0) {
5589 if((prev & 0x80000000)==0 && prev+BE2W(ebn->be_blocks) == key && BE2W(ebn->be_blocks)+blocks < 65536) {
5591 /* Extent we are inserting is mergeable with its previous extent. */
5593 preparecachebuffer(cb);
5595 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)+blocks); /* This merges the previous and the new extent */
5597 if((errorcode=storecachebuffer(cb))==0) {
5598 errorcode=mergeextent(BE2L(ebn->be_key));
5601 else {
5603 /* Extent we are inserting couldn't be merged with its previous extent. */
5605 if((errorcode=setprevextent(prev, key))==0 && (errorcode=createextentbnode(key, &cb, &ebn))==0) {
5607 /* Succesfully updated previous extent, or the object. Also created new BNode */
5609 ebn->be_key=L2BE(key);
5610 ebn->be_prev=L2BE(prev);
5611 ebn->be_next=L2BE(next);
5612 ebn->be_blocks=BE2W(blocks);
5614 if((errorcode=storecachebuffer(cb))==0) {
5615 if((errorcode=setnextextent(next, key))==0) {
5616 mergeextent(key);
5623 return(errorcode);
5628 LONG truncateextent(BLCK key, LONG blocks) {
5629 struct CacheBuffer *cb;
5630 struct fsExtentBNode *ebn;
5631 LONG errorcode=0;
5633 /* Truncates the extended by the given amount of blocks.
5634 If the amount is negative then the truncation occurs
5635 at the start, otherwise at the end.
5637 This function returns INTERR_EXTENT if you tried to
5638 truncate to zero blocks (or beyond). Any errorcode
5639 is returned. If blocks is zero, then this function
5640 does nothing.
5642 Mar 20 1999: The truncation at the start could cause
5643 BNode's to get lost (because they are
5644 indexed by their start block). Fixed. */
5646 if(blocks!=0) {
5647 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5648 ULONG b;
5650 b=blocks<0 ? -blocks : blocks;
5652 if(b<BE2W(ebn->be_blocks) && BE2L(ebn->be_key)==key) {
5653 if(blocks<0) {
5654 ULONG next=BE2L(ebn->be_next);
5655 ULONG prev=BE2L(ebn->be_prev);
5656 UWORD blocks=BE2W(ebn->be_blocks)-b;
5658 /* Truncating at the start. */
5660 if((errorcode=deletebnode(globals->block_extentbnoderoot, key))==0) {
5662 key+=b;
5664 if((errorcode=createbnode(globals->block_extentbnoderoot, key, &cb, (struct BNode **)&ebn))==0) {
5665 ebn->be_key=L2BE(key);
5666 ebn->be_next=L2BE(next);
5667 ebn->be_prev=L2BE(prev);
5668 ebn->be_blocks=W2BE(blocks);
5670 if((errorcode=storecachebuffer(cb))==0) {
5671 /* Truncating at start means changing the key value. This
5672 means that the next and previous BNode's must also be
5673 adjusted. */
5675 if((errorcode=setnextextent(next, key))==0) {
5676 errorcode=setprevextent(prev, key);
5682 else {
5684 /* Truncating at the end. */
5686 preparecachebuffer(cb);
5688 ebn->be_blocks=W2BE(BE2W(ebn->be_blocks)-b);
5690 errorcode=storecachebuffer(cb);
5693 else {
5694 errorcode=INTERR_EXTENT;
5699 return(errorcode);
5704 static LONG copy(BLCK source, BLCK dest, ULONG totblocks, UBYTE *optimizebuffer) {
5705 ULONG blocks;
5706 LONG errorcode=0;
5708 /* Low-level function to copy blocks from one place to another. */
5710 while(totblocks!=0) {
5711 blocks=totblocks;
5712 if(blocks > (OPTBUFSIZE>>globals->shifts_block)) {
5713 blocks=OPTBUFSIZE>>globals->shifts_block;
5716 if((errorcode=read(source, optimizebuffer, blocks))!=0) {
5717 break;
5720 if((errorcode=write(dest, optimizebuffer, blocks))!=0) {
5721 break;
5724 totblocks-=blocks;
5725 source+=blocks;
5726 dest+=blocks;
5729 return(errorcode);
5734 LONG deleteextent(struct CacheBuffer *cb, struct fsExtentBNode *ebn) {
5735 BLCK next,prev;
5736 LONG errorcode;
5738 /* Deletes an fsExtentBNode structure and properly relinks the rest of the chain.
5739 No space will be given free.
5741 newtransaction() should have been called prior to calling this function. */
5743 next=BE2L(ebn->be_next);
5744 prev=BE2L(ebn->be_prev);
5746 if((errorcode=deletebnode(globals->block_extentbnoderoot,BE2L(ebn->be_key)))==0) { /*** Maybe use deleteinternalnode here??? */
5747 if((errorcode=setnextextent(next, prev))==0) {
5748 errorcode=setprevextent(prev, next);
5752 return(errorcode);
5757 static WORD enough_for_add_moved(void) {
5758 if(globals->defragmentlongs>5) {
5759 return TRUE;
5761 return FALSE;
5764 static inline void add_moved(ULONG blocks, ULONG from, ULONG to) {
5765 if(globals->defragmentsteps!=0) {
5766 *globals->defragmentsteps++=AROS_LONG2BE(MAKE_ID('M','O','V','E'));
5767 *globals->defragmentsteps++=3;
5768 *globals->defragmentsteps++=blocks;
5769 *globals->defragmentsteps++=from;
5770 *globals->defragmentsteps++=to;
5771 *globals->defragmentsteps=0;
5773 globals->defragmentlongs-=5;
5777 static inline void add_done(void) {
5778 if(globals->defragmentsteps!=0) {
5779 *globals->defragmentsteps++=AROS_LONG2BE(MAKE_ID('D','O','N','E'));
5780 *globals->defragmentsteps++=0;
5781 *globals->defragmentsteps=0;
5783 globals->defragmentlongs-=2;
5789 LONG moveextent(struct fsExtentBNode *ebn, BLCK dest, UWORD blocks) {
5790 UBYTE *buf;
5791 LONG errorcode;
5793 /* This function (partially) moves the Extent to /dest/.
5794 /blocks/ is the number of blocks moved; if blocks is
5795 equal to the size of the Extent, then the Extent will
5796 be deleted. Else the start of the Extent will be
5797 truncated. */
5799 if((buf=AllocVec(OPTBUFSIZE, globals->bufmemtype))!=0) {
5800 BLCK key=BE2L(ebn->be_key);
5801 BLCK next=BE2L(ebn->be_next);
5802 BLCK prev=BE2L(ebn->be_prev);
5803 UWORD blocksinextent=BE2W(ebn->be_blocks);
5805 if((errorcode=copy(key, dest, blocks, buf))==0) { // This functions knows that OPTBUFSIZE is the size of the buffer!
5807 /* Data has been physically moved -- nothing has been
5808 permanently altered yet. */
5810 if((errorcode=markspace(dest, blocks))==0) {
5812 if((errorcode=freespace(key, blocks))==0) {
5814 /* Bitmap has been altered to reflect the new location of the
5815 moved data. Now we either need to truncate the Extent (if
5816 it was partially moved) or remove it. */
5818 if(blocksinextent==blocks) {
5819 struct CacheBuffer *cb;
5821 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5822 errorcode=deleteextent(cb, ebn);
5825 else {
5826 next=key+blocks;
5827 errorcode=truncateextent(key, -blocks);
5830 if(errorcode==0) {
5831 if((errorcode=insertextent(dest, next, prev, blocks))==0) {
5832 add_moved(blocks, key, dest);
5839 FreeVec(buf);
5841 else {
5842 errorcode=ERROR_NO_FREE_STORE;
5845 return(errorcode);
5850 static LONG fillgap(BLCK key) {
5851 struct CacheBuffer *cb;
5852 struct fsExtentBNode *ebn;
5853 LONG errorcode;
5855 /* This function will attempt to fill the gap behind an extent with
5856 data from the next extent. This in effect merges the extent and
5857 the next extent (partially). */
5859 if((errorcode=findextentbnode(key, &cb, &ebn))==0) {
5860 ULONG next=BE2L(ebn->be_next);
5862 key+=BE2W(ebn->be_blocks);
5864 while(next!=0 && (errorcode=findextentbnode(next, &cb, &ebn))==0) { // !! failed !!
5865 UWORD blocks=BE2W(ebn->be_blocks);
5866 LONG free;
5868 lockcachebuffer(cb);
5870 if((free=availablespace(key, 1024))>0) {
5872 unlockcachebuffer(cb);
5874 _DEBUG(("fillgap: availablespace() returned %ld\n",free));
5876 /* The gap consists of /free/ blocks. */
5878 if(free > blocks && enough_for_add_moved()!=FALSE) {
5879 next=BE2L(ebn->be_next);
5881 else {
5882 next=0;
5885 if((errorcode=moveextent(ebn, key, MIN(free, blocks)))!=0) {
5886 return(errorcode);
5889 key+=blocks;
5891 else {
5892 unlockcachebuffer(cb);
5897 _DEBUG(("fillgap: exiting with errorcode %ld\n",errorcode));
5899 return(errorcode);
5904 LONG getbnode(BLCK block, struct CacheBuffer **returned_cb, struct fsExtentBNode **returned_ebn) {
5905 LONG errorcode;
5907 /* This function gets the ExtentBNode which starts at the given
5908 block or the first one after the given block. Zero *ebn indicates
5909 there were no ExtentBNode's at or after the given block. */
5911 if((errorcode=findbnode(globals->block_extentbnoderoot, block, returned_cb, (struct BNode **)returned_ebn))==0) {
5913 _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)));
5915 if(*returned_ebn!=0 && BE2L((*returned_ebn)->be_key)<block) {
5916 errorcode=nextbnode(globals->block_extentbnoderoot, returned_cb, (struct BNode **)returned_ebn);
5918 _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)));
5922 return(errorcode);
5927 #if 0
5928 LONG makefreespace(BLCK block) {
5929 struct CacheBuffer *cb;
5930 struct fsExtentBNode *ebn;
5931 LONG errorcode;
5933 /* This function tries to move the data located at /block/ to
5934 another area of the disk. */
5936 if((errorcode=findextentbnode(block, &cb, &ebn))==0) {
5937 ULONG blocks;
5938 ULONG newblocks;
5939 BLCK startblock;
5941 lockcachebuffer(cb);
5943 blocks=MIN(OPTBUFSIZE>>shifts_block, ebn->blocks);
5945 if((errorcode=findspace2_backwards(blocks, block, blocks_total, &startblock, &newblocks))==0) {
5946 unlockcachebuffer(cb);
5948 if(newblocks!=0) {
5950 _DEBUG(("makefreespace: Looking for %ld blocks from block %ld, and found %ld blocks at block %ld.\n", blocks, block, newblocks, startblock));
5952 errorcode=moveextent(ebn, startblock, newblocks);
5954 else {
5955 errorcode=ERROR_DISK_FULL;
5958 else {
5959 unlockcachebuffer(cb);
5963 return(errorcode);
5965 #endif
5968 LONG makefreespace(BLCK block) {
5969 struct CacheBuffer *cb;
5970 struct fsExtentBNode *ebn;
5971 LONG errorcode;
5972 WORD count=0;
5974 /* This function tries to move the data located at /block/ to
5975 another area of the disk. */
5977 if((errorcode=findextentbnode(block, &cb, &ebn))==0) {
5978 do {
5979 ULONG blocks;
5980 ULONG newblocks;
5981 BLCK startblock;
5983 lockcachebuffer(cb);
5985 blocks=MIN(OPTBUFSIZE>>globals->shifts_block, BE2W(ebn->be_blocks));
5987 if((errorcode=findspace2_backwards(blocks, BE2L(ebn->be_key), globals->blocks_total, &startblock, &newblocks))==0) { // ebn->key should not be changed to block.
5988 unlockcachebuffer(cb);
5990 if(newblocks!=0) {
5992 _DEBUG(("makefreespace: Looking for %ld blocks from block %ld, and found %ld blocks at block %ld.\n", blocks, block, newblocks, startblock));
5994 if((errorcode=moveextent(ebn, startblock, newblocks))!=0) {
5995 break;
5998 else {
5999 if(count==0) {
6000 errorcode=ERROR_DISK_FULL;
6002 break;
6005 else {
6006 unlockcachebuffer(cb);
6007 break;
6010 if(count++>=1) {
6011 break;
6014 if((errorcode=getbnode(block, &cb, &ebn))!=0 || ebn==0) {
6015 break;
6018 } while((BE2L(ebn->be_prev) & 0x80000000)==0);
6021 return(errorcode);
6026 LONG skipunmoveable(BLCK block) {
6027 struct CacheBuffer *cb;
6028 struct fsExtentBNode *ebn;
6030 /* This function looks for moveable data or free space starting
6031 from the given block. It returns -1 in case of failure, or
6032 the first moveable block it finds. If there ain't no more
6033 moveable blocks, then blocks_total is returned. */
6035 if((findbnode(globals->block_extentbnoderoot, block, &cb, (struct BNode **)&ebn))==0) {
6036 if(ebn!=0 && ebn->be_key==L2BE(block)) {
6037 return((LONG)block);
6039 else if(ebn==0 || BE2L(ebn->be_key)>=block || nextbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn)==0) {
6040 if(ebn!=0) {
6041 BLCK key=BE2L(ebn->be_key);
6042 LONG used;
6044 /* Found something moveable, but maybe there was some free space before the
6045 moveable Extent. */
6047 if((used=allocatedspace(block, key-block))!=-1) {
6048 if(block+used<key) {
6049 return((LONG)block+used);
6051 else {
6052 return((LONG)key);
6056 else {
6057 LONG errorcode;
6059 if((errorcode=findspace(1, block, globals->blocks_total, &block))==0) {
6060 return((LONG)block);
6062 else if(errorcode==ERROR_DISK_FULL) {
6063 return((LONG)globals->blocks_total);
6069 return(-1);
6073 struct fsExtentBNode *startofextentbnodechain(struct fsExtentBNode *ebn) {
6074 struct CacheBuffer *cb;
6075 LONG errorcode=0;
6077 while((BE2L(ebn->be_prev) & 0x80000000)==0 && (errorcode=findextentbnode(BE2L(ebn->be_prev), &cb, &ebn))==0) {
6080 if(errorcode==0) {
6081 return(ebn);
6084 return(0);
6089 #if 0
6090 LONG findmatch(BLCK startblock, ULONG blocks, ULONG *bestkey) {
6091 struct CacheBuffer *cb;
6092 struct fsExtentBNode *ebn;
6093 // struct fsExtentBNode *ebn_start;
6094 ULONG bestblocks=0;
6095 LONG errorcode;
6097 /* This function looks for the start of a ExtentBNode chain
6098 which matches the given size. If none is found, then the
6099 next smaller chain is returned. If none is found, then a
6100 larger chain is returned. If there are no chains at all
6101 0 is returned.
6103 The first ExtentBNode examined is determined by the start
6104 block number which is passed. */
6106 _DEBUG(("findmatch: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
6108 *bestkey=0;
6110 if((errorcode=getbnode(startblock, &cb, &ebn))==0 && ebn!=0) {
6112 _DEBUG(("findmatch: 1, ebn->key = %ld, ebn->blocks = %ld\n", ebn->key, ebn->blocks));
6114 do {
6115 struct CacheBuffer *cb2;
6116 struct fsExtentBNode *ebn_start=ebn;
6117 struct fsObject *o;
6118 ULONG total=ebn->blocks; /* If larger than bestblocks, then we can stop looking for the start of the ExtentBNode chain early. */
6119 ULONG key, newblocks;
6121 lockcachebuffer(cb);
6122 // ebn_start=startofextentbnodechain(ebn);
6124 struct CacheBuffer *cb;
6126 while((total<bestblocks || bestblocks==0) && (ebn_start->prev & 0x80000000)==0 && ebn_start->prev >= ebn->key && (errorcode=findextentbnode(ebn_start->prev, &cb, &ebn_start))==0) {
6127 total+=ebn_start->blocks;
6130 unlockcachebuffer(cb);
6132 if(errorcode!=0) {
6133 break;
6136 if((total<bestblocks || bestblocks==0) && (ebn_start->prev & 0x80000000)!=0) {
6138 _DEBUG(("findmatch: 2, ebn->key = %ld\n", ebn->key));
6140 key=ebn_start->key;
6142 lockcachebuffer(cb);
6143 errorcode=readobject(ebn_start->prev & 0x7FFFFFFF, &cb2, &o);
6144 unlockcachebuffer(cb);
6146 if(errorcode!=0) {
6147 break;
6150 _DEBUG(("findmatch: 3, filesize = %ld\n",o->object.file.size));
6152 newblocks=(o->object.file.size+bytes_block-1)>>shifts_block;
6154 if(newblocks==blocks) { /* Found ideal match */
6155 *bestkey=key;
6156 break;
6158 else if(newblocks<blocks) {
6159 if(newblocks>bestblocks || bestblocks>blocks) {
6160 *bestkey=key;
6161 bestblocks=newblocks;
6164 else if(bestblocks==0 || newblocks<bestblocks) {
6165 *bestkey=key;
6166 bestblocks=newblocks;
6170 } while((errorcode=nextbnode(block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0);
6173 return(errorcode);
6175 #endif
6180 void newfragmentinit_large(ULONG blocks) {
6181 globals->bestkey=0;
6182 globals->bestblocks=0;
6183 globals->searchedblocks=blocks;
6188 ULONG newfragment_large(ULONG block, ULONG blocks) {
6189 if(blocks==globals->searchedblocks) { /* Found ideal match */
6190 return(block);
6192 else if(blocks<globals->searchedblocks) {
6193 if(blocks>globals->bestblocks || globals->bestblocks>globals->searchedblocks) {
6194 globals->bestkey=block;
6195 globals->bestblocks=blocks;
6198 else if(globals->bestblocks==0 || blocks<globals->bestblocks) {
6199 globals->bestkey=block;
6200 globals->bestblocks=blocks;
6203 return(0);
6208 #if 0
6209 ULONG newfragment_large(ULONG block, ULONG blocks, UBYTE type) {
6210 if(type==0) {
6211 if(blocks==searchedblocks) { /* Found ideal match */
6212 return(block);
6214 else if(blocks<searchedblocks) {
6215 if(blocks>bestblocks || bestblocks>searchedblocks) {
6216 bestkey=block;
6217 bestblocks=blocks;
6220 else if(bestblocks==0 || blocks<bestblocks) {
6221 bestkey=block;
6222 bestblocks=blocks;
6226 return(0);
6228 #endif
6231 ULONG newfragmentend_large(void) {
6232 return(globals->bestkey);
6238 void newfragmentinit_small(ULONG blocks) {
6239 ULONG *f=globals->fragment;
6240 WORD n=FRAGMENTS;
6242 newfragmentinit_large(blocks);
6244 while(--n>=0) {
6245 *f++=0;
6254 void newfragmentinit_small(ULONG blocks) {
6255 ULONG *f=fragment;
6256 UBYTE *fb=fragmenttype;
6257 WORD n=FRAGMENTS;
6259 newfragmentinit_large(blocks);
6261 while(--n>=0) {
6262 *f++=0;
6263 *fb++=0;
6269 ULONG newfragment_small(ULONG block, ULONG blocks, UBYTE type) {
6270 if(blocks<searchedblocks) {
6271 if(fragment[blocks]==0 || (fragmenttype[blocks]!=0 && type==0)) {
6272 ULONG blocks2=searchedblocks-blocks;
6274 fragment[blocks]=block;
6275 fragmenttype[blocks]=type;
6277 if(blocks2==blocks) {
6278 blocks2=0;
6281 if(fragment[blocks2]!=0) {
6282 if(fragmenttype[blocks]==0 && fragmenttype[blocks2]==0) {
6283 return(fragment[blocks]);
6289 return(newfragment_large(block, blocks, type));
6295 ULONG newfragment_small(ULONG block, ULONG blocks) {
6296 if(blocks<globals->searchedblocks && globals->fragment[blocks]==0) {
6297 ULONG blocks2=globals->searchedblocks-blocks;
6299 globals->fragment[blocks]=block;
6301 if(blocks2==blocks) {
6302 blocks2=0;
6305 if(globals->fragment[blocks2]!=0) {
6306 return(globals->fragment[blocks]);
6310 return(newfragment_large(block, blocks));
6315 ULONG newfragmentend_small(void) {
6316 return(newfragmentend_large());
6322 #if 0
6323 LONG findmatch(BLCK startblock, ULONG blocks, ULONG *bestkey) {
6324 struct CacheBuffer *cb;
6325 struct fsExtentBNode *ebn;
6326 ULONG lastextentend=0;
6327 LONG maxscan=defrag_maxfilestoscan;
6328 LONG errorcode;
6330 /* This function looks for the start of a ExtentBNode chain
6331 which matches the given size. If none is found, then the
6332 next smaller chain is returned. If none is found, then a
6333 larger chain is returned. If there are no chains at all
6334 0 is returned.
6336 The first ExtentBNode examined is determined by the start
6337 block number which is passed. */
6339 _DEBUG(("findmatch: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
6341 *bestkey=0;
6343 if(blocks>FRAGMENTS-1) {
6344 newfragmentinit_large(blocks);
6346 else {
6347 newfragmentinit_small(blocks);
6350 if((errorcode=getbnode(startblock, &cb, &ebn))==0 && ebn!=0) {
6352 _DEBUG(("findmatch: ebn->key = %ld, ebn->blocks = %ld\n", ebn->key, ebn->blocks));
6354 do {
6355 if((ebn->prev & 0x80000000)!=0) {
6356 struct CacheBuffer *cb2;
6357 struct fsObject *o;
6358 ULONG newblocks;
6359 UBYTE fragmenttype;
6361 /* Found the start of a candidate chain. */
6363 lockcachebuffer(cb);
6364 errorcode=readobject(ebn->prev & 0x7FFFFFFF, &cb2, &o);
6365 unlockcachebuffer(cb);
6367 if(errorcode!=0) {
6368 break;
6371 newblocks=(o->object.file.size+bytes_block-1)>>shifts_block;
6373 fragmenttype=lastextentend==ebn->key ? 1 : 0;
6375 if(blocks>FRAGMENTS-1) {
6376 *bestkey=newfragment_large(ebn->key, newblocks, fragmenttype);
6378 else {
6379 *bestkey=newfragment_small(ebn->key, newblocks, fragmenttype);
6382 if(*bestkey!=0) {
6383 return(0);
6386 if(--maxscan<0) {
6387 break;
6390 lastextentend=ebn->next==0 ? ebn->key + ebn->blocks : 0;
6392 } while((errorcode=nextbnode(block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0);
6394 if(errorcode==0) {
6395 if(blocks>FRAGMENTS-1) {
6396 *bestkey=newfragmentend_large();
6398 else {
6399 *bestkey=newfragmentend_small();
6404 return(errorcode);
6406 #endif
6410 LONG findmatch_fromend(BLCK startblock, ULONG blocks, ULONG *bestkey) {
6411 struct CacheBuffer *cb;
6412 struct fsExtentBNode *ebn;
6413 LONG maxscan=globals->defrag_maxfilestoscan;
6414 LONG errorcode;
6416 /* This function looks for the start of a ExtentBNode chain
6417 which matches the given size. If none is found, then the
6418 next smaller chain is returned. If none is found, then a
6419 larger chain is returned. If there are no chains at all
6420 0 is returned.
6422 The first ExtentBNode examined is determined by the start
6423 block number which is passed. */
6425 _DEBUG(("findmatch_fromend: Looking for a chain of %ld blocks starting from block %ld\n", blocks, startblock));
6427 *bestkey=0;
6429 if(blocks>FRAGMENTS-1) {
6430 newfragmentinit_large(blocks);
6432 else {
6433 newfragmentinit_small(blocks);
6436 if((errorcode=lastbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0 && BE2L(ebn->be_key)>=startblock) {
6438 _DEBUG(("findmatch_fromend: ebn->key = %ld, ebn->blocks = %ld\n", BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
6440 do {
6441 if((BE2L(ebn->be_prev) & 0x80000000)!=0) { // Is this a 'first fragment' of something?
6442 struct CacheBuffer *cb2;
6443 struct fsObject *o;
6444 ULONG newblocks;
6446 _DEBUG(("findmatch_fromend!: ebn->key = %ld, ebn->blocks = %ld\n", BE2L(ebn->be_key), BE2W(ebn->be_blocks)));
6448 /* Found the start of a candidate chain. */
6450 lockcachebuffer(cb);
6451 errorcode=readobject(BE2L(ebn->be_prev) & 0x7FFFFFFF, &cb2, &o);
6452 unlockcachebuffer(cb);
6454 if(errorcode!=0) {
6455 break;
6458 newblocks=(BE2L(o->object.file.be_size)+globals->bytes_block-1)>>globals->shifts_block;
6460 if(blocks>FRAGMENTS-1) {
6461 *bestkey=newfragment_large(BE2L(ebn->be_key), newblocks);
6463 else {
6464 *bestkey=newfragment_small(BE2L(ebn->be_key), newblocks);
6467 if(*bestkey!=0) {
6468 return(0);
6471 if(--maxscan<0) {
6472 break;
6475 } while((errorcode=previousbnode(globals->block_extentbnoderoot, &cb, (struct BNode **)&ebn))==0 && ebn!=0 && BE2L(ebn->be_key)>=startblock);
6477 if(errorcode==0) {
6478 if(blocks>FRAGMENTS-1) {
6479 *bestkey=newfragmentend_large();
6481 else {
6482 *bestkey=newfragmentend_small();
6485 else {
6486 _DEBUG(("findmatch_fromend: errorcode=%ld\n", errorcode));
6490 return(errorcode);
6495 LONG step(void) {
6496 LONG errorcode;
6497 LONG free;
6499 if((free=availablespace(globals->block_defragptr, 256))!=-1) {
6501 _DEBUG(("Defragmenter: Found %ld blocks of free space at block %ld.\n", free, globals->block_defragptr));
6503 if(free==0) {
6504 struct CacheBuffer *cb;
6505 struct fsExtentBNode *ebn;
6507 /* Determine in which extent block_defragptr is located. */
6509 if((errorcode=findbnode(globals->block_extentbnoderoot, globals->block_defragptr, &cb, (struct BNode **)&ebn))==0) {
6510 if(ebn==0 || BE2L(ebn->be_key)!=globals->block_defragptr) {
6511 LONG block;
6513 _DEBUG(("Defragmenter: Found unmoveable data at block %ld.\n", globals->block_defragptr));
6515 /* Skip unmoveable data */
6517 if((block=skipunmoveable(globals->block_defragptr))!=-1) {
6518 globals->block_defragptr=block;
6520 else {
6521 errorcode=INTERR_DEFRAGMENTER;
6524 else if((BE2L(ebn->be_prev) & 0x80000000)!=0 || BE2L(ebn->be_prev)<globals->block_defragptr) {
6526 _DEBUG(("Defragmenter: Found a (partially) defragmented extent at block %ld.\n", globals->block_defragptr));
6528 if(BE2L(ebn->be_next)==0 || BE2L(ebn->be_next) == BE2L(ebn->be_key)+BE2W(ebn->be_blocks)) {
6529 /* If there is no next Extent, or if the next Extent is touching
6530 this one, then skip the current one. */
6532 _DEBUG(("Defragmenter: Extent has no next or next is touching this one.\n"));
6534 globals->block_defragptr+=BE2W(ebn->be_blocks);
6536 else {
6537 LONG freeafter;
6538 BLCK key=BE2L(ebn->be_key);
6539 BLCK next=BE2L(ebn->be_next);
6540 UWORD blocks=BE2W(ebn->be_blocks);
6542 _DEBUG(("Defragmenter: Extent has a next extent.\n"));
6544 if((freeafter=availablespace(key+blocks, 256))!=-1) {
6546 _DEBUG(("Defragmenter: There are %ld blocks of free space after the extent at block %ld.\n", freeafter, key));
6548 if(freeafter>0) {
6549 /* Move (part of) data located in next extent to this free space. */
6551 _DEBUG(("Defragmenter: Filling the gap.\n"));
6553 /* The function below can be called multiple times in a row. When there is a large
6554 gap in which multiple extents of the file will fit, then these can all be transfered
6555 into the gap at once. */
6557 errorcode=fillgap(key);
6559 else {
6560 LONG block;
6562 /* Determine which extent it is which is located directly after the current extent. */
6564 if((block=skipunmoveable(key+blocks))!=-1) {
6565 if(block==key+blocks) {
6567 _DEBUG(("Defragmenter: There was a moveable extent after the extent at block %ld.\n", key));
6569 /* There was no unmoveable data, so let's move it. */
6571 errorcode=makefreespace(block);
6573 else {
6574 LONG freeafter;
6576 _DEBUG(("Defragmenter: Skipped %ld blocks of unmoveable data.\n", block-(key+blocks)));
6578 /* Unmoveable data was skipped. */
6580 if(block!=next) { // Mar 20 1999: Check to see if the extent after the unmoveable data is not already the correct one.
6582 if((freeafter=availablespace(block, 256))!=-1) {
6584 _DEBUG(("Defragmenter: There are %ld blocks of free space after the unmoveable data.\n", freeafter));
6586 if(freeafter==0) {
6588 _DEBUG(("Defragmenter: Clearing some space at block %ld.\n", block));
6590 if((errorcode=makefreespace(block))==0) {
6591 if((freeafter=availablespace(block, 256))==-1) {
6592 errorcode=INTERR_DEFRAGMENTER;
6595 _DEBUG(("Defragmenter: There are now %ld blocks of cleared space after the unmoveable data.\n", freeafter));
6599 if(errorcode==0) {
6600 struct CacheBuffer *cb;
6601 struct fsExtentBNode *ebn;
6603 if((errorcode=findextentbnode(next, &cb, &ebn))==0) {
6605 _DEBUG(("Defragmenter: Moved next extent of our extent directly after the unmoveable space (block = %ld).\n", block));
6607 if((errorcode=moveextent(ebn, block, MIN(freeafter, BE2W(ebn->be_blocks))))==0) {
6608 globals->block_defragptr=block;
6613 else {
6614 errorcode=INTERR_DEFRAGMENTER;
6617 else {
6619 /* Extent after unmoveable data is already correct, so no need to move it. */
6621 globals->block_defragptr=block;
6625 else {
6626 errorcode=INTERR_DEFRAGMENTER;
6630 else {
6631 errorcode=INTERR_DEFRAGMENTER;
6635 else {
6637 _DEBUG(("Defragmenter: Found an extent at block %ld which must be moved away.\n", globals->block_defragptr));
6639 errorcode=makefreespace(globals->block_defragptr);
6643 else {
6644 ULONG bestkey;
6646 if((errorcode=findmatch_fromend(globals->block_defragptr, free, &bestkey))==0) {
6647 if(bestkey!=0) {
6648 struct CacheBuffer *cb;
6649 struct fsExtentBNode *ebn;
6651 if((errorcode=findextentbnode(bestkey, &cb, &ebn))==0) {
6653 _DEBUG(("Defragmenter: Moving a new first Extent to %ld\n", globals->block_defragptr));
6655 errorcode=moveextent(ebn, globals->block_defragptr, MIN(free, BE2W(ebn->be_blocks)));
6658 else {
6659 _DEBUG(("Defragmenter: Nothing more to optimize.\n"));
6661 add_done();
6666 else {
6667 errorcode=INTERR_DEFRAGMENTER;
6670 _DEBUG(("Defragmenter: Exiting with errorcode %ld\n\n",errorcode));
6672 return(errorcode);
6679 if (block at block_optimizeptr is full) {
6681 determine in which extent block_optimizeptr is located.
6683 if (not located in extent) {
6684 skip unmoveable data: add 1 to block_optimizeptr.
6686 else if (extent is first extent OR previous extent is located before block_optimizeptr) {
6687 if (extent is last extent (as well)) {
6688 file was defragmented: set block_optimizeptr to point just after this extent.
6690 else {
6691 determine if there is free space located after this extent.
6693 if (there is free space) {
6694 move (part of) data located in next extent to this free space.
6696 else {
6698 determine which extent it is which is located directly after the current extent.
6700 if (no such extent exists) {
6701 skip unmoveable data
6703 if (no free space after unmoveable data) {
6704 make some free space by moving data.
6706 move (part of) data located in next extent to the (newly made) free space: set block_optimizeptr in this part.
6708 else {
6709 make some free space first by moving data.
6714 else {
6715 make some free space by moving data at block_optimizeptr.
6718 else {
6720 determine amount of free space.
6722 look for an extent-chain equal to the amount of free space beyond this location.
6724 if (found suitable extent-chain) {
6725 move data to block_optimizeptr; set block_optimizeptr to point just after this data.
6727 else {
6728 scan for ANY 'first' extent beyond this location.
6730 if (none found) {
6731 optimization is done.
6733 else {
6734 move found 'first' extent to block_optimizeptr.
6743 /* The simple purpose of the SFS DosList handler task is to
6744 provide the following non-blocking functions:
6746 - Adding a VolumeNode to the DosList
6747 data = VolumeNode
6749 - Removing a VolumeNode from the DosList (synchronously)
6750 data = VolumeNode
6752 All messages send to the DosList handler are freed by
6753 the DosList handler itself. This is to avoid having to
6754 wait for the reply and then free the message yourself.
6757 #ifdef __AROS__
6758 #undef globals
6759 #else
6760 #undef SysBase
6761 #endif
6762 #undef DOSBase
6764 static void sdlhtask(void)
6766 #ifndef __AROS__
6767 struct ExecBase *SysBase=globals->sysBase;
6768 #endif
6769 struct DosLibrary *DOSBase;
6771 if((DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37))!=0) {
6772 Forbid();
6773 if(FindPort("SFS DosList handler")==0) {
6774 struct MsgPort *port;
6776 if((port=CreateMsgPort())!=0) {
6777 struct SFSMessage *sfsm;
6779 port->mp_Node.ln_Name="SFS DosList handler";
6780 port->mp_Node.ln_Pri=1;
6781 AddPort(port);
6782 Permit();
6784 for(;;) {
6785 struct DosList *dol;
6787 WaitPort(port);
6789 dol=LockDosList(LDF_WRITE|LDF_VOLUMES);
6791 while((sfsm=(struct SFSMessage *)GetMsg(port))!=0) {
6792 if(sfsm->command==SFSM_ADD_VOLUMENODE) {
6793 /* AddDosEntry rejects volumes based on their name and date. */
6795 if(AddDosEntry((struct DosList *)sfsm->data)==DOSFALSE) {
6796 sfsm->errorcode=IoErr();
6799 // if(AddDosEntry((struct DosList *)sfsm->data)==DOSFALSE) {
6800 // errorcode=IoErr();
6801 // FreeDosEntry((struct DosList *)vn);
6802 // vn=0;
6803 // }
6805 else if(sfsm->command==SFSM_REMOVE_VOLUMENODE) {
6806 struct DosList *vn=(struct DosList *)sfsm->data;
6808 while((dol=NextDosEntry(dol, LDF_VOLUMES))!=0) {
6809 if(dol==vn) {
6810 RemDosEntry(dol);
6811 break;
6814 // removevolumenode(dol, (struct DosList *)sfsm->data); /* Dangerous because of DOSBase?? */
6815 FreeDosEntry(vn);
6818 FreeVec(sfsm);
6821 UnLockDosList(LDF_WRITE|LDF_VOLUMES);
6824 else {
6825 Permit();
6828 else {
6829 Permit();
6832 CloseLibrary((struct Library *)DOSBase);